UKDS logo
UKDS logo

Guide to Interactive Visualisations

In this guide, you’ll be shown how to make 4 key interactive visualisations, which include:

To create these visualisations, we’ll be using the ‘plotly’ package.

Datasets used in this workshop are from the 2021 UK census, and involve the new voluntary question which focuses on gender identity. In particular, we explore the relationship between age and gender ethnicity, as well as ethnicity and gender identity.

Let’s get started by importing the necessary packages.

NOTE: If you’re not following along with Binder, and you have your own computational environment, make sure you install the necessary packages through the command line before proceeding to import.

Install packages

Uncomment the lines below to install the packages if you’re not working in Binder.

# install.packages("readr")
# install.packages("dplyr")
# install.packages("stringr")
# install.packages("shiny")
# install.packages("ggplot2")
# install.packages("plotly")

Load in packages

# Allows us to read-in csv files
library(readr) 
# For data manipulation
library(dplyr) 

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
# For regular expression operations 
library(stringr) 
# library(shiny)
library(ggplot2)
# Used tp create interactive visualisations
library(plotly)

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

Dataset 1

The first dataset that we’ll be focusing on is a really simple dataset which shows the total counts for 8 gender identity categories across England and Wales. We’ll do a bit of data cleaning, remove unnecessary categories (such as ‘Does not apply’), and then calculate the % of each gender identity category. Then, we’ll create a simple interactive bar chart which displays the percentage by gender identity category, whilst enabling some interactivity when we hover over each bar.

# Load in dataset

df <- read_csv('Data/GI_det_EW.csv')
Rows: 8 Columns: 5── Column specification ───────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (3): England and Wales Code, England and Wales, Gender identity (8 categories)
dbl (2): Gender identity (8 categories) Code, Observation
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Brief glimpse of data structure
# But can also click on the dataset in the Environment pane
head(df, 10)
# Let's check out the dimensions
dim(df)
[1] 8 5

Data cleaning

  • Clean column names
  • Filter out unecessary categories
  • Clean gender identity category values - too wordy
  • Ensure gender_identity column is a factor with levels in desired order
# str_replace_all() method finds all substrings which match the regex and replaces them with empty string
# First, let's replace any brackets with empty strings
colnames(df) <- str_replace_all(colnames(df), "\\s*\\([^)]*\\)", "")

# Lowercase column text and replace empty spaces with "_"
colnames(df) <- tolower(colnames(df))
colnames(df) <- str_replace_all(colnames(df), " ", "_")

# Let's see if it worked..
colnames(df)
[1] "england_and_wales_code" "england_and_wales"      "gender_identity_code"   "gender_identity"       
[5] "observation"           

Pipes and other operators..

So, we’ve already come across the assignment operator ‘<-’ which is used to assign a value. E.g. df <- read_csv(‘Data/GI_age.csv’), here we assign our csv file to a dataframe variable called ‘df’.

But, we’re now going to encounter the pipe operator ‘%>%’ which can seem intimidating at first but is actually pretty simple. It’s used to pass the result of one function directly into the next function. E.g. df <- df %>% filter(gender_identity_code != -8), here we start with our df and pass it to the filter function using the pipe operator. This basically supplies the filter() function with its first argument, which is the dataframe to filter on. And here we encounter a logical operator ‘!=’ within the filter() function, which specifies that we should only keep rows where gender_identity_code is not equal to -8.

Dollar sign operator - $

This operator is used to access elements, such as columns of a dataframe, by name. Below, we use it to access the gender identity code column, where we want to view the unique values.

# Get rid of columns that do not apply
df <- df %>% 
  filter(gender_identity_code != -8) 

# Use unique and access column to output its unique values

unique(df$gender_identity_code)
[1] 1 2 3 4 5 6 7
# Let's take a look at our unique values in our gender_identity category column

unique(df$gender_identity)
[1] "Gender identity the same as sex registered at birth"                                  
[2] "Gender identity different from sex registered at birth but no specific identity given"
[3] "Trans woman"                                                                          
[4] "Trans man"                                                                            
[5] "Non-binary"                                                                           
[6] "All other gender identities"                                                          
[7] "Not answered"                                                                         
# Use combo of mutate and recode to replace multiple values in column
# .default ensures that any value not matching those specified are left unchanged

df <- df %>%
  mutate(gender_identity = recode(gender_identity,
    "Gender identity the same as sex registered at birth" = "Cisgender",
    "Gender identity different from sex registered at birth but no specific identity given" = "Gender identity different from sex",
    .default = gender_identity))
# Let's see if it worked...
unique(df$gender_identity)
[1] "Cisgender"                          "Gender identity different from sex" "Trans woman"                       
[4] "Trans man"                          "Non-binary"                         "All other gender identities"       
[7] "Not answered"                      
# We use factor to convert gender_identity column to a factor with specified levels 
# This tells Plotly the exact order in which to display categories

df$gender_identity <- factor(df$gender_identity, levels = c(
  "Cisgender",
  "Gender identity different from sex",
  "Trans woman",
  "Trans man",
  "All other gender identities",
  "Not answered"
))

Question

How is gender identity represented across England and Wales?

Some subquestions that this can help us answer:

  • Which gender identity category is most prevalent?
  • Which gender identity is the least prevalent?

Data pre-processing

Before we can plot our data, we need to calculate the percentage of each gender identity category. The mutate() function adds a new column ‘percentage’ to df, and applies the following calculation to each row.

# mutate() is used to add new variables to a df or modify existing ones
df <- df %>% mutate(percentage = round(observation / sum(observation) * 100, 2))
# Let's take a look..
head(df$percentage)
[1] 93.46  0.24  0.10  0.10  0.06  0.04

Basic interactive bar chart

Now we can create our first simple interactive visualisation. To do so we use Plotly’s plot_ly function, and supply the parameters with the necessary arguments. You’ll notice that we use the tilde operator (~) quite a bit when building our graph. By preceding relevant variables with ~ it tells R to look for that variable within the dataframe.

# Create the bar chart visualization with percentages on the y-axis
fig <- plot_ly(data = df, x = ~gender_identity, y = ~percentage, type = 'bar',
               # defines how the bars should be styled
               marker = list(color = 'rgb(158,202,225)', line = list(color = 'rgb(8,48,107)', width = 1.5)),
               width = 800, height = 600)
# Let's check it out
fig
Warning: Ignoring 1 observationsWarning: Ignoring 1 observations

Using layout() method

Once a graph has been created, we can use the layout method to customise the appearance and layout. This allows you to modify things such as titles, legend details, axis properties, etc, without needing to recreate the figure from scratch.

# Let's apply a log scale to our y-axis so this graph is easier to interpet 

fig <- layout(fig,
              title = 'Percentage of Each Gender Identity in England and Wales',
              # set showline to true, otherwise it disappears when we apply log scale
              xaxis = list(title = 'Gender Identity', showline = TRUE),
              yaxis = list(type = 'log', title = 'Percentage (Log Scale)'))
fig
Warning: Ignoring 1 observationsWarning: Ignoring 1 observations

Tooltips

When using different R libraries that are geared towards interactive visualisations, you’ll often come across ‘tooltips’. These are small boxes that provide information when a user hovers over a part of a data visualisation such as: a point on a graph, a bar in a bar chart, or a segment in a pie chart. They are used to display additional information about the data point or object, providing more context without cluttering up the chart. In Plotly tooltips are referred to as ‘hover_data’.

All interactive plotly graphs come with default hover data, so when you scroll over a bar or a scatterplot data point it will display the specific x-axis value and y-axis value. But, variety is the spice of life and there’s going to be times when you want to leverage this feature to include interesting info that isn’t included by default. For instance, for our bar chart, I’d like to add in data from the ‘Observation’ column, which shows the raw count for each gender identity category.

To do this it’s quite easy. We use the text and hoverinfo parameter in the plot_ly function, with text defining the variables we’d like to include and how they should appear, and hoverinfo ensuring that this text is displayed in the tooltips. So, let’s create the graph again, but this time let’s specify our tooltips.

new_fig <- plot_ly(data = df, x = ~gender_identity, y = ~percentage, type = 'bar',
                   # ~paste  combines multiple pieces of text and data into one string
               hovertext = ~paste("Gender: ", gender_identity, 
                                  #<br> is HTML code for a line break
                                  # sprintf - used to format strings
                             "<br>Percentage: ", sprintf("%.2f%%", percentage), 
                             "<br>Observations: ", observation),
               # tells plotly to only display the text provided in hovertext
               hoverinfo = 'text',
               marker = list(color = 'rgb(158,202,225)', line = list(color = 'rgb(8,48,107)', width = 1.5)),
               width = 800, height = 600)

# Apply a log scale to the y-axis
new_fig <- layout(new_fig,
              title = 'Percentage of Each Gender Identity in England and Wales',
              xaxis = list(title = 'Gender Identity', showline = TRUE),
              yaxis = list(type = 'log', title = 'Percentage (Log Scale)'))
new_fig
Warning: Ignoring 1 observationsWarning: Ignoring 1 observations

Dataset 2

This dataset classifies residents by gender identity and age, with the unit of analysis being England and Wales.

# Load in dataset 

df2 <- read_csv('Data/GI_age.csv')
Rows: 42 Columns: 7── Column specification ───────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): England and Wales Code, England and Wales, Gender identity (7 categories), Age (6 categories)
dbl (3): Gender identity (7 categories) Code, Age (6 categories) Code, Observation
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Brief glimpse of data structure
head(df2, 10)
# Let's check out the dimensions

dim(df2)
[1] 42  7

Data Cleaning

  • Clean column names
  • Filter out unecessary categories
  • Clean gender identity category values - too wordy
  • Ensure gender_identity column is a factor with levels in desired order
  • Clean age category values - too wordy

We’ll whiz through this, because it’s the same stuff we did for the last dataset.

# str_replace_all() method finds all substrings which match the regex and replaces them with empty string
# First, let's replace any brackets with empty strings
colnames(df2) <- str_replace_all(colnames(df2), "\\s*\\([^)]*\\)", "")

# Lowercase column text and replace empty spaces with "_"
colnames(df2) <- tolower(colnames(df2))
colnames(df2) <- str_replace_all(colnames(df2), " ", "_")

# Let's see if it worked..
colnames(df2)
[1] "england_and_wales_code" "england_and_wales"      "gender_identity_code"   "gender_identity"       
[5] "age_code"               "age"                    "observation"           
# Get rid of columns that do not apply
df2 <- df2 %>% 
  filter(gender_identity_code != -8) 

# Use unique and access column to output its unique values

unique(df2$gender_identity_code)
[1] 1 2 3 4 5 6
# Get rid of redundant age category
# Further filter data
df2 <- df2 %>%
  filter(age_code != 1)
# Clean up the values in the 'age' column. Let's shorten them.

# Chain str_replace() calls together to apply multiple string replacements in succession
# Each str_replace() call is applied to the result of the previous one
df2$age <- df2$age %>%
  str_replace('Aged ', '') %>%
  str_replace('to', '-') %>%
  str_replace('years', '') %>%
  str_replace('and over', '+') %>%
  str_replace(' - ', '-')

# We can pass our df to the select function, where we specify the column we're interested in.
# Then, we pipe the output to the head function.
df2 %>%
  select(age) %>%
  head()
# Use combo of mutate and recode to replace multiple values in column
# .default ensures that any value not matching those specified are left unchanged

df2 <- df2 %>%
  mutate(gender_identity = recode(gender_identity,
    "Gender identity the same as sex registered at birth" = "Cisgender",
    "Gender identity different from sex registered at birth but no specific identity given" = "Gender identity different from sex",
    .default = gender_identity))

unique(df2$gender_identity)
[1] "Cisgender"                          "Gender identity different from sex" "Trans woman"                       
[4] "Trans man"                          "All other gender identities"        "Not answered"                      
# We use factor to convert gender_identity column to a factor with specified levels 
# This tells Plotly the exact order in which to display categories

df2$gender_identity <- factor(df2$gender_identity, levels = c(
  "Cisgender",
  "Gender identity different from sex",
  "Trans woman",
  "Trans man",
  "All other gender identities",
  "Not answered"
))

Question

How is gender identity distributed among different age groups?

Some subquestions that this can help us answer:

  • What % of trans women are aged 16-24 years?
  • Are older age groups over represented in the ‘non-response’ category?

Data pre-processing

Calculate percentages

Below, we use the group_by function to group the data by ‘gender_identity’ and calculate the percentage within each group. Then the mutate() function adds a new column ‘percentage’ to df, which (for each group) divides the observation by the sum of observations, multiplies it by 100, and rounds it up to 2 decimal points. We then use the ungroup function when we’re done with the grouping operation.

df2 <- df2 %>%
  group_by(gender_identity) %>%
  mutate(percentage = round((observation / sum(observation) * 100), 2)) %>%
  ungroup()

head(df2)

Interactive grouped bar chart

When creating grouped bar charts, there’s a few subtle differences that you’ll need to account for in the code. First, we’ll need to make sure this is a grouped bar chart, which we can set with the ‘barmode’ parameter. Second, we’ll need a way to colour each bar in each group, according to age categories, which we can do with the ‘color’ and ‘colors’ parameters.

# Create a grouped bar chart with hover information
fig2 <- plot_ly(data = df2, x = ~gender_identity, y = ~percentage, type = 'bar',
               # color specifies which variable to colour by
               # colors specifies the colour palette to use, and how many colours are required
               color = ~age, colors = RColorBrewer::brewer.pal(length(unique(df2$age)), "Set2"),
               hoverinfo = 'text',
               hovertext = ~paste("Observation: ", observation,
                                  "<br>Percentage: ", sprintf("%.2f%%", percentage),
                                  "<br>Age group: ", age),
               marker = list(line = list(color = 'rgba(255,255,255, 0.5)', width = 0.5)),
               width = 800, height = 600)
               
fig2
fig2 <- layout(fig2,title = 'Distribution of Gender Identity Categories Among Age Groups',
         xaxis = list(title = 'Gender Identity'),
         yaxis = list(title = 'Percentage'),
         legend = list(title = list(text = 'Age Group')))
fig2

Stacked bar chart

The method I show below simply converts the previously made grouped bar chart ‘fig2’ to a stacked bar chart. Stacked bar charts can only be created using the layout() function to change the barmode, as the default is a grouped bar chart.

# Convert to stacked bar chart

st_fig <- layout(fig2,
         barmode = 'stack')

st_fig

Dataset 3

This dataset classifies residents by gender identity and ethnic group, with the unit of analysis being the 331 local authorities across England and Wales.

# Load in dataset

df3 <- read_csv('Data/GI_ethnic.csv')
Rows: 10592 Columns: 7── Column specification ───────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): Lower tier local authorities Code, Lower tier local authorities, Gender identity (4 categories), Ethni...
dbl (3): Gender identity (4 categories) Code, Ethnic group (8 categories) Code, Observation
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Brief glimpse at underlying data structure
head(df3, 10)

Data Cleaning

  • Clean column names
  • Filter out unnecessary categories

Below, I provide another method ‘gsub()’ which can be used instead of the str_replace_all() method which has been demonstrated in the previous cleaning sections. Basically, looks for a pattern and applies the replacement to any column names which match the pattern.

# Remove all text within parentheses from column names and replace it with an empty string

# tilde operator (~) used to apply function 'gsub' to each colname
# .x represents each colname that gsub will be applied to
df3 <- df3 %>% 
  rename_with(~ gsub("\\s*\\([^)]*\\)", "", .x))
# Lowercase all text in column names and replace spaces with underscores
df3 <- df3 %>% 
  rename_with(~ tolower(gsub(" ", "_", .x)))
# Shorten the local authority column names as they are way too long
df3 <- df3 %>% 
  rename(LA_code = lower_tier_local_authorities_code,
         LA_name = lower_tier_local_authorities)
# Let's see if it worked
colnames(df3)
[1] "LA_code"              "LA_name"              "gender_identity_code" "gender_identity"     
[5] "ethnic_group_code"    "ethnic_group"         "observation"         
# Remove 'Does not apply' categories for the gender identity and ethnic group columns
df3 <- df3 %>% 
  filter(gender_identity_code != -8, ethnic_group_code != -8)
# Let's see if it worked.. 
unique(df3$gender_identity_code)
[1] 1 2 3
# Let's see if it worked..
unique(df3$ethnic_group_code)
[1] 1 2 3 4 5 6 7

Question

How does the rate of ‘non-response’ on gender identity vary among different ethnic groups across local authorities in England and Wales?

A subquestion this could help us answer:

Does the relationship between non-response and ethnic group % for local authorities differ between the ‘White’ categories and other ethnic groups?

Data pre-processing

Given that I want to explore the question above, I’d like to create a scatterplot which explores the relationship between the % of certain ethnic groups within local authorities and their non-response rates. Therefore, I’ll need to prep my x and y variables, so I’ll need to calculate the percentage of each ethnic group in each LA, and that ethnic groups non-response rate within each LA.

Calculate % of each ethnic group in each LA

# First, we're going to group our data by LA_name, ethnic group, and sum our observations
# This leaves us with the total of each ethnic group in each local authority
ethnic_totals <- df3 %>%
  group_by(LA_name, ethnic_group) %>%
  summarise(Ethnic_sum = sum(observation, na.rm = TRUE)) %>%
  ungroup()
`summarise()` has grouped output by 'LA_name'. You can override using the `.groups` argument.
# Print the first few rows to check
head(ethnic_totals)
# Calculate total observations for each local authority by grouping df3 by local authority and summing up obs
la_totals <- df3 %>%
  group_by(LA_name) %>%
  summarise(LA_sum = sum(observation, na.rm = TRUE)) %>%
  ungroup()

# Print the first few rows to check
head(la_totals)
# Merge the ethnic_totals and la_totals dataframes together
# by parameter specifies which column to perform merge on

grp_pct <- merge(ethnic_totals, la_totals, by = "LA_name")
# Calculate the percentage of each ethnic group within each local authority
# Store results in new column 

grp_pct <- grp_pct %>%
  mutate(Percentage = round((Ethnic_sum / LA_sum * 100), 2))
# Print the first few rows to check
head(grp_pct, 10)

Calculate Ethnic Group Non-Response Rates (%’s) Within LAs

# We already have our ethnic group totals which we can re-use...

ethnic_totals
# Calculate sum of non-responses for each ethnic group within each LA
# Filter df3 so that we only have non-response rows
# Group by LA and ethnic group then sum non-response obs and store the results in new column

non_response_totals <- df3 %>%
  filter(gender_identity == 'Not answered') %>%
  group_by(LA_name, ethnic_group) %>%
  summarise(NR_total = sum(observation, na.rm = TRUE)) %>%
  ungroup()
`summarise()` has grouped output by 'LA_name'. You can override using the `.groups` argument.

# Let's check it out.. 
head(non_response_totals)
# Merge the tethnic group totals with the ethnic group non-response totals
# c - used when we're referencing more than one column
# all.x - performs a left join
grp_nr <- merge(ethnic_totals, non_response_totals, by = c("LA_name", "ethnic_group"), all.x = TRUE)
# Let's check it out.. 

head(grp_nr)
# Calculate the non-response percentage for each ethnic group within each LA
# Store results in new column

grp_nr <- grp_nr %>%
  mutate(Eth_NR_Perc = round((NR_total / Ethnic_sum * 100), 2))
# Quick glance.. 
head(grp_nr)

Merge both datasets

Now that we’ve completed the necessary calculations, we are left with two datasets:

  • grp_pct - details the % of each ethnic_group in each LA
  • grp_nr - details the ethnic group non-response % in each LA

All we need to do now then, is merge these datasets together so that we can access the new columns and plot them:

  • Percentage
  • Eth_NR_Perc
# Merge the non-response data with the percentage of each ethnic group within each LA
# Use select to isolate columns I want to preserve in the merge, LA_sum is redundant...

nr <- merge(grp_nr, select(grp_pct, LA_name, ethnic_group, Percentage), by = c("LA_name", "ethnic_group"))
# Quick glance

head(nr)

Interactive scatterplot

In this section we’re going to:

  1. Create a simple scatterplot exploring the relationship between the percentage of asian citizens within local authorities and their non-response rates

  2. Implement a dropdown widget to update our scatterplot

# Subset dataframe so we only have responses from the asian ethnic group

asian <- nr %>%
  filter(ethnic_group == 'Asian, Asian British or Asian Welsh')
# Check it out.. 

head(asian)
# Initialize figure
fig3 <- plot_ly(data = asian,
               x = ~Percentage,
               y = ~Eth_NR_Perc,
               text = ~paste('LA Name:', LA_name,
                             '<br>Non-response Total:', NR_total,
                             '<br>Ethnic Group Total:', Ethnic_sum),
               hoverinfo = "text",
               mode = 'markers',  # Specify marker points
               type = 'scatter',  # Graph type - scatterplot
               name = 'Asian')  # Default visible graph


# Customize layout 
fig3 <- fig3 %>%
  layout(title = 'Non-Response Rates of the Asian Ethnic Group Across Local Authorities',
         xaxis = list(title = 'Percentage of Ethnic Group'),
         yaxis = list(title = 'Non-response Rate'),
         width = 700,
         height = 700)
Warning: Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()
# Show the plot
fig3

Dataset 3

This dataset includes sexual identity estimates by gender from 2010 to 2014. This is presented at a UK level, and broken down by England, Wales, Scotland and Northern Ireland. I wanted this guide to include a demo of how to make interactive line graphs with gender identity data, but unfortunately given this is only the first year that the ONS has collected this data that was not possible. So I found a dataset from 2015 which involves experimental statistics that have been used in the Integrated Household Survey. For more info, you can check out this ONS link.

# Load in dataset

df3 <- read_csv('Data/cleaned_sexuality_df.csv')
Rows: 300 Columns: 5── Column specification ───────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (3): Country, Gender, Sexuality
dbl (2): Year, Percentage
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Brief glimpse at underlying data structure

head(df3, 10)

Data cleaning

When I first found this dataset it was very messy and formatted terribly, so I performed some cleaning on it in a separate jupyter notebook, to save cluttering this one and distracting from the main tutorial. If you’d like to see how I cleaned it up, please see the ‘Data_cleaning_sexuality.ipynb’ notebook.

Data pre-processing

The only pre-processing we’re going to do is subset our data by country, and also create 2 separate datasets for Gender = Men and Gender = Women. I’ll explain why this step is needed soon.

# Subset dataframe so we only have responses from the white ethnic group
england_df <- df3 %>%
  filter(Country == 'England')
# Let's check it out.. 

unique(england_df$Country)
[1] "England"
# Further filter data for each gender

men <- england_df %>% filter(Gender == "Men")
women <- england_df %>% filter(Gender == "Women")

# Let's check it worked

unique(men$Gender)
[1] "Men"
unique(women$Gender)
[1] "Women"

Interactive linegraph

Creating a simple line graph in plotly is pretty easy, but where plotly struggles (in R) is in handling facet plots. A facet plot is a type of visualisation that divides data into subplots based on categorical variables. What I’d like to do is create a facet plot of sexuality percentages in England (2010-2014) with individual subplots for our two genders. This is achieved easily in Python due to the plotly.express module, which provides a simple way to create facet plots. Unfortunately, we’ll have to go through a bit more of a longwinded route, where we’ll manually create our individual plots for each gender, then combine them using the subplot function. Also, plotly.express automatically manages legends to ensure they’re unified across facets, but R’s plotly requires that we manually sync up these legends. Womp womp. Let’s get to it.

# Create individual plot for each gender

# Create plots for each gender
men_plot <- plot_ly(men, 
                    x = ~Year, 
                    y = ~Percentage, 
                    color = ~Sexuality, 
                    type = 'scatter', 
                    mode = 'lines+markers', 
                    hoverinfo = 'text',
                    text = ~paste("Year:", Year, "<br>Percentage:", Percentage, "<br>Sexuality:", Sexuality),
                    legendgroup = ~Sexuality,
                    showlegend = TRUE) %>%
  layout(xaxis = list(title = 'Year', tickvals = 2010:2014, ticktext = 2010:2014),
         yaxis = list(title = 'Percentage'),
         annotations = list(
           list(x = 0.5, y = 1.05, text = "Men", showarrow = FALSE, xref='paper', yref='paper')))


women_plot <- plot_ly(women, 
                      x = ~Year, 
                      y = ~Percentage, 
                      color = ~Sexuality, 
                      type = 'scatter', 
                      mode = 'lines+markers', 
                      hoverinfo = 'text',
                      text = ~paste("Year:", Year, "<br>Percentage:", Percentage, "<br>Sexuality:", Sexuality),
                      legendgroup = ~Sexuality,
                      showlegend = FALSE) %>%
  layout(xaxis = list(title = 'Year', tickvals = 2010:2014, ticktext = 2010:2014),
         yaxis = list(title = 'Percentage'),
         annotations = list(
           list(x = 0.5, y = 1.05, text = "Women", showarrow = FALSE, xref='paper', yref='paper')))

# Let's take a look at one of these graphs

women_plot
# Combine individual plots using subplot
# Within subplot, define number of rows, make sure share same x axes and both axes titles
fig5 <- subplot(men_plot, women_plot, nrows = 2, shareX = TRUE, titleX = TRUE, titleY = TRUE) %>%
  layout(
    title = list(
      text = 'Sexuality Percentages by Gender in England (2010-2014)', 
      y = 0.98,  # Move the title higher up
      x = 0.5,   # Center the title
      xanchor = "center",
      yanchor = "top"
    ),
    margin = list(t = 100),  # Add space at the top for the title
    height = 800,
    width = 1000
  )
Warning: Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()
fig5

Sharing your interactive graphs online

I’m going to provide you first with a really simple way to host Plotly graphs specifically, then we’ll look into other more complex options that work with many visualisation packages.

  1. Use Plotly’s ‘Chart Studio’. You can upload your visualisations directly from your coding environment and then get a link to share them online. You’ll need to sign up for an account but it’s free, unless you want to share the link privately then you’ll need to upgrade your account. Otherwise, for data that’s fine being out in the open, this is a good option.

  2. Embed your graphs in GitHub pages.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIVtVS0RTIGxvZ29dKC9Vc2Vycy9sb3VjYXAvRG9jdW1lbnRzL0dpdFdvcmsvSW50ZXJhY3RpdmVfdmlzX25ldy9JbWFnZXMvdWtkcy5wbmcpCgojIEd1aWRlIHRvIEludGVyYWN0aXZlIFZpc3VhbGlzYXRpb25zCgpJbiB0aGlzIGd1aWRlLCB5b3UnbGwgYmUgc2hvd24gaG93IHRvIG1ha2UgNCBrZXkgaW50ZXJhY3RpdmUgdmlzdWFsaXNhdGlvbnMsIHdoaWNoIGluY2x1ZGU6IAoKKiBCYXNpYyBiYXIgY2hhcnQKKiBHcm91cGVkIGJhciBjaGFydAoqIFN0YWNrZWQgYmFyIGNoYXJ0CiogU2NhdHRlcnBsb3QKKiBTY2F0dGVycGxvdCArIGRyb3Bkb3duIG1lbnUKKiBMaW5lIGdyYXBoCgpUbyBjcmVhdGUgdGhlc2UgdmlzdWFsaXNhdGlvbnMsIHdlJ2xsIGJlIHVzaW5nIHRoZSAqKidwbG90bHknKiogcGFja2FnZS4gCgpEYXRhc2V0cyB1c2VkIGluIHRoaXMgd29ya3Nob3AgYXJlIGZyb20gdGhlIDIwMjEgVUsgY2Vuc3VzLCBhbmQgaW52b2x2ZSB0aGUgbmV3IHZvbHVudGFyeSBxdWVzdGlvbiB3aGljaCBmb2N1c2VzIG9uIGdlbmRlciBpZGVudGl0eS4gSW4gcGFydGljdWxhciwgd2UgZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYWdlIGFuZCBnZW5kZXIgZXRobmljaXR5LCBhcyB3ZWxsIGFzIGV0aG5pY2l0eSBhbmQgZ2VuZGVyIGlkZW50aXR5LiAKCkxldCdzIGdldCBzdGFydGVkIGJ5IGltcG9ydGluZyB0aGUgbmVjZXNzYXJ5IHBhY2thZ2VzLgoKKipOT1RFOioqIElmIHlvdSdyZSBub3QgZm9sbG93aW5nIGFsb25nIHdpdGggQmluZGVyLCBhbmQgeW91IGhhdmUgeW91ciBvd24gY29tcHV0YXRpb25hbCBlbnZpcm9ubWVudCwgbWFrZSBzdXJlIHlvdSBpbnN0YWxsIHRoZSBuZWNlc3NhcnkgcGFja2FnZXMgdGhyb3VnaCB0aGUgY29tbWFuZCBsaW5lIGJlZm9yZSBwcm9jZWVkaW5nIHRvIGltcG9ydC4gCgojIyBJbnN0YWxsIHBhY2thZ2VzCgpVbmNvbW1lbnQgdGhlIGxpbmVzIGJlbG93IHRvIGluc3RhbGwgdGhlIHBhY2thZ2VzIGlmIHlvdSdyZSBub3Qgd29ya2luZyBpbiBCaW5kZXIuCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkciIpCiMgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQojIGluc3RhbGwucGFja2FnZXMoInN0cmluZ3IiKQojIGluc3RhbGwucGFja2FnZXMoInNoaW55IikKIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQpgYGAKCiMjIExvYWQgaW4gcGFja2FnZXMKCmBgYHtyfQojIEFsbG93cyB1cyB0byByZWFkLWluIGNzdiBmaWxlcwpsaWJyYXJ5KHJlYWRyKSAKIyBGb3IgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeShkcGx5cikgCiMgRm9yIHJlZ3VsYXIgZXhwcmVzc2lvbiBvcGVyYXRpb25zIApsaWJyYXJ5KHN0cmluZ3IpIAojIGxpYnJhcnkoc2hpbnkpCmxpYnJhcnkoZ2dwbG90MikKIyBVc2VkIHRwIGNyZWF0ZSBpbnRlcmFjdGl2ZSB2aXN1YWxpc2F0aW9ucwpsaWJyYXJ5KHBsb3RseSkKYGBgCgojIERhdGFzZXQgMQoKVGhlIGZpcnN0IGRhdGFzZXQgdGhhdCB3ZSdsbCBiZSBmb2N1c2luZyBvbiBpcyBhIHJlYWxseSBzaW1wbGUgZGF0YXNldCB3aGljaCBzaG93cyB0aGUgdG90YWwgY291bnRzIGZvciA4IGdlbmRlciBpZGVudGl0eSBjYXRlZ29yaWVzIGFjcm9zcyBFbmdsYW5kIGFuZCBXYWxlcy4gV2UnbGwgZG8gYSBiaXQgb2YgZGF0YSBjbGVhbmluZywgcmVtb3ZlIHVubmVjZXNzYXJ5IGNhdGVnb3JpZXMgKHN1Y2ggYXMgJ0RvZXMgbm90IGFwcGx5JyksIGFuZCB0aGVuIGNhbGN1bGF0ZSB0aGUgJSBvZiBlYWNoIGdlbmRlciBpZGVudGl0eSBjYXRlZ29yeS4gVGhlbiwgd2UnbGwgY3JlYXRlIGEgc2ltcGxlIGludGVyYWN0aXZlIGJhciBjaGFydCB3aGljaCBkaXNwbGF5cyB0aGUgcGVyY2VudGFnZSBieSBnZW5kZXIgaWRlbnRpdHkgY2F0ZWdvcnksIHdoaWxzdCBlbmFibGluZyBzb21lIGludGVyYWN0aXZpdHkgd2hlbiB3ZSBob3ZlciBvdmVyIGVhY2ggYmFyLgoKYGBge3J9CiMgTG9hZCBpbiBkYXRhc2V0CgpkZiA8LSByZWFkX2NzdignRGF0YS9HSV9kZXRfRVcuY3N2JykKYGBgCmBgYHtyfQojIEJyaWVmIGdsaW1wc2Ugb2YgZGF0YSBzdHJ1Y3R1cmUKIyBCdXQgY2FuIGFsc28gY2xpY2sgb24gdGhlIGRhdGFzZXQgaW4gdGhlIEVudmlyb25tZW50IHBhbmUKaGVhZChkZiwgMTApCmBgYAoKYGBge3J9CiMgTGV0J3MgY2hlY2sgb3V0IHRoZSBkaW1lbnNpb25zCmRpbShkZikKYGBgCgojIyBEYXRhIGNsZWFuaW5nCgoqIENsZWFuIGNvbHVtbiBuYW1lcwoqIEZpbHRlciBvdXQgdW5lY2Vzc2FyeSBjYXRlZ29yaWVzCiogQ2xlYW4gZ2VuZGVyIGlkZW50aXR5IGNhdGVnb3J5IHZhbHVlcyAtIHRvbyB3b3JkeQoqIEVuc3VyZSBnZW5kZXJfaWRlbnRpdHkgY29sdW1uIGlzIGEgZmFjdG9yIHdpdGggbGV2ZWxzIGluIGRlc2lyZWQgb3JkZXIKCgpgYGB7cn0KIyBzdHJfcmVwbGFjZV9hbGwoKSBtZXRob2QgZmluZHMgYWxsIHN1YnN0cmluZ3Mgd2hpY2ggbWF0Y2ggdGhlIHJlZ2V4IGFuZCByZXBsYWNlcyB0aGVtIHdpdGggZW1wdHkgc3RyaW5nCiMgRmlyc3QsIGxldCdzIHJlcGxhY2UgYW55IGJyYWNrZXRzIHdpdGggZW1wdHkgc3RyaW5ncwpjb2xuYW1lcyhkZikgPC0gc3RyX3JlcGxhY2VfYWxsKGNvbG5hbWVzKGRmKSwgIlxccypcXChbXildKlxcKSIsICIiKQoKIyBMb3dlcmNhc2UgY29sdW1uIHRleHQgYW5kIHJlcGxhY2UgZW1wdHkgc3BhY2VzIHdpdGggIl8iCmNvbG5hbWVzKGRmKSA8LSB0b2xvd2VyKGNvbG5hbWVzKGRmKSkKY29sbmFtZXMoZGYpIDwtIHN0cl9yZXBsYWNlX2FsbChjb2xuYW1lcyhkZiksICIgIiwgIl8iKQoKIyBMZXQncyBzZWUgaWYgaXQgd29ya2VkLi4KY29sbmFtZXMoZGYpCgpgYGAKCgojIyMgUGlwZXMgYW5kIG90aGVyIG9wZXJhdG9ycy4uCgpTbywgd2UndmUgYWxyZWFkeSBjb21lIGFjcm9zcyB0aGUgYXNzaWdubWVudCBvcGVyYXRvciAnPC0nIHdoaWNoIGlzIHVzZWQgdG8gYXNzaWduIGEgdmFsdWUuIEUuZy4gZGYgPC0gcmVhZF9jc3YoJ0RhdGEvR0lfYWdlLmNzdicpLCBoZXJlIHdlIGFzc2lnbiBvdXIgY3N2IGZpbGUgdG8gYSBkYXRhZnJhbWUgdmFyaWFibGUgY2FsbGVkICdkZicuCgpCdXQsIHdlJ3JlIG5vdyBnb2luZyB0byBlbmNvdW50ZXIgdGhlIHBpcGUgb3BlcmF0b3IgJyU+JScgd2hpY2ggY2FuIHNlZW0gaW50aW1pZGF0aW5nIGF0IGZpcnN0IGJ1dCBpcyBhY3R1YWxseSBwcmV0dHkgc2ltcGxlLiBJdCdzIHVzZWQgdG8gcGFzcyB0aGUgcmVzdWx0IG9mIG9uZSBmdW5jdGlvbiBkaXJlY3RseSBpbnRvIHRoZSBuZXh0IGZ1bmN0aW9uLiBFLmcuIGRmIDwtIGRmICU+JSBmaWx0ZXIoZ2VuZGVyX2lkZW50aXR5X2NvZGUgIT0gLTgpLCBoZXJlIHdlIHN0YXJ0IHdpdGggb3VyIGRmIGFuZCBwYXNzIGl0IHRvIHRoZSBmaWx0ZXIgZnVuY3Rpb24gdXNpbmcgdGhlIHBpcGUgb3BlcmF0b3IuIFRoaXMgYmFzaWNhbGx5IHN1cHBsaWVzIHRoZSBmaWx0ZXIoKSBmdW5jdGlvbiB3aXRoIGl0cyBmaXJzdCBhcmd1bWVudCwgd2hpY2ggaXMgdGhlIGRhdGFmcmFtZSB0byBmaWx0ZXIgb24uIEFuZCBoZXJlIHdlIGVuY291bnRlciBhIGxvZ2ljYWwgb3BlcmF0b3IgJyE9JyB3aXRoaW4gdGhlIGZpbHRlcigpIGZ1bmN0aW9uLCB3aGljaCBzcGVjaWZpZXMgdGhhdCB3ZSBzaG91bGQgb25seSBrZWVwIHJvd3Mgd2hlcmUgZ2VuZGVyX2lkZW50aXR5X2NvZGUgaXMgbm90IGVxdWFsIHRvIC04LiAKCiMjIyBEb2xsYXIgc2lnbiBvcGVyYXRvciAtICQKClRoaXMgb3BlcmF0b3IgaXMgdXNlZCB0byBhY2Nlc3MgZWxlbWVudHMsIHN1Y2ggYXMgY29sdW1ucyBvZiBhIGRhdGFmcmFtZSwgYnkgbmFtZS4KQmVsb3csIHdlIHVzZSBpdCB0byBhY2Nlc3MgdGhlIGdlbmRlciBpZGVudGl0eSBjb2RlIGNvbHVtbiwgd2hlcmUgd2Ugd2FudCB0byB2aWV3IHRoZSB1bmlxdWUgdmFsdWVzLgoKYGBge3J9CiMgR2V0IHJpZCBvZiBjb2x1bW5zIHRoYXQgZG8gbm90IGFwcGx5CmRmIDwtIGRmICU+JSAKICBmaWx0ZXIoZ2VuZGVyX2lkZW50aXR5X2NvZGUgIT0gLTgpIAoKIyBVc2UgdW5pcXVlIGFuZCBhY2Nlc3MgY29sdW1uIHRvIG91dHB1dCBpdHMgdW5pcXVlIHZhbHVlcwoKdW5pcXVlKGRmJGdlbmRlcl9pZGVudGl0eV9jb2RlKQpgYGAKCmBgYHtyfQojIExldCdzIHRha2UgYSBsb29rIGF0IG91ciB1bmlxdWUgdmFsdWVzIGluIG91ciBnZW5kZXJfaWRlbnRpdHkgY2F0ZWdvcnkgY29sdW1uCgp1bmlxdWUoZGYkZ2VuZGVyX2lkZW50aXR5KQpgYGAKYGBge3J9CiMgVXNlIGNvbWJvIG9mIG11dGF0ZSBhbmQgcmVjb2RlIHRvIHJlcGxhY2UgbXVsdGlwbGUgdmFsdWVzIGluIGNvbHVtbgojIC5kZWZhdWx0IGVuc3VyZXMgdGhhdCBhbnkgdmFsdWUgbm90IG1hdGNoaW5nIHRob3NlIHNwZWNpZmllZCBhcmUgbGVmdCB1bmNoYW5nZWQKCmRmIDwtIGRmICU+JQogIG11dGF0ZShnZW5kZXJfaWRlbnRpdHkgPSByZWNvZGUoZ2VuZGVyX2lkZW50aXR5LAogICAgIkdlbmRlciBpZGVudGl0eSB0aGUgc2FtZSBhcyBzZXggcmVnaXN0ZXJlZCBhdCBiaXJ0aCIgPSAiQ2lzZ2VuZGVyIiwKICAgICJHZW5kZXIgaWRlbnRpdHkgZGlmZmVyZW50IGZyb20gc2V4IHJlZ2lzdGVyZWQgYXQgYmlydGggYnV0IG5vIHNwZWNpZmljIGlkZW50aXR5IGdpdmVuIiA9ICJHZW5kZXIgaWRlbnRpdHkgZGlmZmVyZW50IGZyb20gc2V4IiwKICAgIC5kZWZhdWx0ID0gZ2VuZGVyX2lkZW50aXR5KSkKYGBgCgpgYGB7cn0KIyBMZXQncyBzZWUgaWYgaXQgd29ya2VkLi4uCnVuaXF1ZShkZiRnZW5kZXJfaWRlbnRpdHkpCmBgYAoKYGBge3J9CiMgV2UgdXNlIGZhY3RvciB0byBjb252ZXJ0IGdlbmRlcl9pZGVudGl0eSBjb2x1bW4gdG8gYSBmYWN0b3Igd2l0aCBzcGVjaWZpZWQgbGV2ZWxzIAojIFRoaXMgdGVsbHMgUGxvdGx5IHRoZSBleGFjdCBvcmRlciBpbiB3aGljaCB0byBkaXNwbGF5IGNhdGVnb3JpZXMKCmRmJGdlbmRlcl9pZGVudGl0eSA8LSBmYWN0b3IoZGYkZ2VuZGVyX2lkZW50aXR5LCBsZXZlbHMgPSBjKAogICJDaXNnZW5kZXIiLAogICJHZW5kZXIgaWRlbnRpdHkgZGlmZmVyZW50IGZyb20gc2V4IiwKICAiVHJhbnMgd29tYW4iLAogICJUcmFucyBtYW4iLAogICJBbGwgb3RoZXIgZ2VuZGVyIGlkZW50aXRpZXMiLAogICJOb3QgYW5zd2VyZWQiCikpCmBgYAoKIyMgUXVlc3Rpb24KCkhvdyBpcyBnZW5kZXIgaWRlbnRpdHkgcmVwcmVzZW50ZWQgYWNyb3NzIEVuZ2xhbmQgYW5kIFdhbGVzPwoKU29tZSBzdWJxdWVzdGlvbnMgdGhhdCB0aGlzIGNhbiBoZWxwIHVzIGFuc3dlcjoKCiogV2hpY2ggZ2VuZGVyIGlkZW50aXR5IGNhdGVnb3J5IGlzIG1vc3QgcHJldmFsZW50PwoqIFdoaWNoIGdlbmRlciBpZGVudGl0eSBpcyB0aGUgbGVhc3QgcHJldmFsZW50PwoKIyMgRGF0YSBwcmUtcHJvY2Vzc2luZwoKQmVmb3JlIHdlIGNhbiBwbG90IG91ciBkYXRhLCB3ZSBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSBvZiBlYWNoIGdlbmRlciBpZGVudGl0eSBjYXRlZ29yeS4gClRoZSBtdXRhdGUoKSBmdW5jdGlvbiBhZGRzIGEgbmV3IGNvbHVtbiAncGVyY2VudGFnZScgdG8gZGYsIGFuZCBhcHBsaWVzIHRoZSBmb2xsb3dpbmcgY2FsY3VsYXRpb24gdG8gZWFjaCByb3cuCgpgYGB7cn0KIyBtdXRhdGUoKSBpcyB1c2VkIHRvIGFkZCBuZXcgdmFyaWFibGVzIHRvIGEgZGYgb3IgbW9kaWZ5IGV4aXN0aW5nIG9uZXMKZGYgPC0gZGYgJT4lIG11dGF0ZShwZXJjZW50YWdlID0gcm91bmQob2JzZXJ2YXRpb24gLyBzdW0ob2JzZXJ2YXRpb24pICogMTAwLCAyKSkKYGBgCgpgYGB7cn0KIyBMZXQncyB0YWtlIGEgbG9vay4uCmhlYWQoZGYkcGVyY2VudGFnZSkKYGBgCgoKCiMjIEJhc2ljIGludGVyYWN0aXZlIGJhciBjaGFydAoKTm93IHdlIGNhbiBjcmVhdGUgb3VyIGZpcnN0IHNpbXBsZSBpbnRlcmFjdGl2ZSB2aXN1YWxpc2F0aW9uLiBUbyBkbyBzbyB3ZSB1c2UgUGxvdGx5J3MgcGxvdF9seSBmdW5jdGlvbiwgYW5kIHN1cHBseSB0aGUgcGFyYW1ldGVycyB3aXRoIHRoZSBuZWNlc3NhcnkgYXJndW1lbnRzLiBZb3UnbGwgbm90aWNlIHRoYXQgd2UgdXNlIHRoZSB0aWxkZSBvcGVyYXRvciAofikgcXVpdGUgYSBiaXQgd2hlbiBidWlsZGluZyBvdXIgZ3JhcGguIEJ5IHByZWNlZGluZyByZWxldmFudCB2YXJpYWJsZXMgd2l0aCB+IGl0IHRlbGxzIFIgdG8gbG9vayBmb3IgdGhhdCB2YXJpYWJsZSB3aXRoaW4gdGhlIGRhdGFmcmFtZS4KCgpgYGB7cn0KIyBDcmVhdGUgdGhlIGJhciBjaGFydCB2aXN1YWxpemF0aW9uIHdpdGggcGVyY2VudGFnZXMgb24gdGhlIHktYXhpcwpmaWcgPC0gcGxvdF9seShkYXRhID0gZGYsIHggPSB+Z2VuZGVyX2lkZW50aXR5LCB5ID0gfnBlcmNlbnRhZ2UsIHR5cGUgPSAnYmFyJywKICAgICAgICAgICAgICAgIyBkZWZpbmVzIGhvdyB0aGUgYmFycyBzaG91bGQgYmUgc3R5bGVkCiAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAncmdiKDE1OCwyMDIsMjI1KScsIGxpbmUgPSBsaXN0KGNvbG9yID0gJ3JnYig4LDQ4LDEwNyknLCB3aWR0aCA9IDEuNSkpLAogICAgICAgICAgICAgICB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQpgYGAKCgpgYGB7cn0KIyBMZXQncyBjaGVjayBpdCBvdXQKZmlnCgpgYGAKCiMjIFVzaW5nIGxheW91dCgpIG1ldGhvZAoKT25jZSBhIGdyYXBoIGhhcyBiZWVuIGNyZWF0ZWQsIHdlIGNhbiB1c2UgdGhlIGxheW91dCBtZXRob2QgdG8gY3VzdG9taXNlIHRoZSBhcHBlYXJhbmNlIGFuZCBsYXlvdXQuIFRoaXMgYWxsb3dzIHlvdSB0byBtb2RpZnkgdGhpbmdzIHN1Y2ggYXMgdGl0bGVzLCBsZWdlbmQgZGV0YWlscywgYXhpcyBwcm9wZXJ0aWVzLCBldGMsIHdpdGhvdXQgbmVlZGluZyB0byByZWNyZWF0ZSB0aGUgZmlndXJlIGZyb20gc2NyYXRjaC4KCmBgYHtyfQojIExldCdzIGFwcGx5IGEgbG9nIHNjYWxlIHRvIG91ciB5LWF4aXMgc28gdGhpcyBncmFwaCBpcyBlYXNpZXIgdG8gaW50ZXJwZXQgCgpmaWcgPC0gbGF5b3V0KGZpZywKICAgICAgICAgICAgICB0aXRsZSA9ICdQZXJjZW50YWdlIG9mIEVhY2ggR2VuZGVyIElkZW50aXR5IGluIEVuZ2xhbmQgYW5kIFdhbGVzJywKICAgICAgICAgICAgICAjIHNldCBzaG93bGluZSB0byB0cnVlLCBvdGhlcndpc2UgaXQgZGlzYXBwZWFycyB3aGVuIHdlIGFwcGx5IGxvZyBzY2FsZQogICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdHZW5kZXIgSWRlbnRpdHknLCBzaG93bGluZSA9IFRSVUUpLAogICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0eXBlID0gJ2xvZycsIHRpdGxlID0gJ1BlcmNlbnRhZ2UgKExvZyBTY2FsZSknKSkKYGBgCgpgYGB7cn0KZmlnCmBgYAoKIyMgVG9vbHRpcHMKCldoZW4gdXNpbmcgZGlmZmVyZW50IFIgbGlicmFyaWVzIHRoYXQgYXJlIGdlYXJlZCB0b3dhcmRzIGludGVyYWN0aXZlIHZpc3VhbGlzYXRpb25zLCB5b3UnbGwgb2Z0ZW4gY29tZSBhY3Jvc3MgJ3Rvb2x0aXBzJy4gVGhlc2UgYXJlIHNtYWxsIGJveGVzIHRoYXQgcHJvdmlkZSBpbmZvcm1hdGlvbiB3aGVuIGEgdXNlciBob3ZlcnMgb3ZlciBhIHBhcnQgb2YgYSBkYXRhIHZpc3VhbGlzYXRpb24gc3VjaCBhczogYSBwb2ludCBvbiBhIGdyYXBoLCBhIGJhciBpbiBhIGJhciBjaGFydCwgb3IgYSBzZWdtZW50IGluIGEgcGllIGNoYXJ0LiBUaGV5IGFyZSB1c2VkIHRvIGRpc3BsYXkgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGF0YSBwb2ludCBvciBvYmplY3QsIHByb3ZpZGluZyBtb3JlIGNvbnRleHQgd2l0aG91dCBjbHV0dGVyaW5nIHVwIHRoZSBjaGFydC4gSW4gUGxvdGx5IHRvb2x0aXBzIGFyZSByZWZlcnJlZCB0byBhcyAnaG92ZXJfZGF0YScuCgpBbGwgaW50ZXJhY3RpdmUgcGxvdGx5IGdyYXBocyBjb21lIHdpdGggZGVmYXVsdCBob3ZlciBkYXRhLCBzbyB3aGVuIHlvdSBzY3JvbGwgb3ZlciBhIGJhciBvciBhIHNjYXR0ZXJwbG90IGRhdGEgcG9pbnQgaXQgd2lsbCBkaXNwbGF5IHRoZSBzcGVjaWZpYyB4LWF4aXMgdmFsdWUgYW5kIHktYXhpcyB2YWx1ZS4gQnV0LCB2YXJpZXR5IGlzIHRoZSBzcGljZSBvZiBsaWZlIGFuZCB0aGVyZSdzIGdvaW5nIHRvIGJlIHRpbWVzIHdoZW4geW91IHdhbnQgdG8gbGV2ZXJhZ2UgdGhpcyBmZWF0dXJlIHRvIGluY2x1ZGUgaW50ZXJlc3RpbmcgaW5mbyB0aGF0IGlzbid0IGluY2x1ZGVkIGJ5IGRlZmF1bHQuIEZvciBpbnN0YW5jZSwgZm9yIG91ciBiYXIgY2hhcnQsIEknZCBsaWtlIHRvIGFkZCBpbiBkYXRhIGZyb20gdGhlICdPYnNlcnZhdGlvbicgY29sdW1uLCB3aGljaCBzaG93cyB0aGUgcmF3IGNvdW50IGZvciBlYWNoIGdlbmRlciBpZGVudGl0eSBjYXRlZ29yeS4KClRvIGRvIHRoaXMgaXQncyBxdWl0ZSBlYXN5LiBXZSB1c2UgdGhlIHRleHQgYW5kIGhvdmVyaW5mbyBwYXJhbWV0ZXIgaW4gdGhlIHBsb3RfbHkgZnVuY3Rpb24sIHdpdGggdGV4dCBkZWZpbmluZyB0aGUgdmFyaWFibGVzIHdlJ2QgbGlrZSB0byBpbmNsdWRlIGFuZCBob3cgdGhleSBzaG91bGQgYXBwZWFyLCBhbmQgaG92ZXJpbmZvIGVuc3VyaW5nIHRoYXQgdGhpcyB0ZXh0IGlzIGRpc3BsYXllZCBpbiB0aGUgdG9vbHRpcHMuIFNvLCBsZXQncyBjcmVhdGUgdGhlIGdyYXBoIGFnYWluLCBidXQgdGhpcyB0aW1lIGxldCdzIHNwZWNpZnkgb3VyIHRvb2x0aXBzLgoKYGBge3J9Cm5ld19maWcgPC0gcGxvdF9seShkYXRhID0gZGYsIHggPSB+Z2VuZGVyX2lkZW50aXR5LCB5ID0gfnBlcmNlbnRhZ2UsIHR5cGUgPSAnYmFyJywKICAgICAgICAgICAgICAgICAgICMgfnBhc3RlICBjb21iaW5lcyBtdWx0aXBsZSBwaWVjZXMgb2YgdGV4dCBhbmQgZGF0YSBpbnRvIG9uZSBzdHJpbmcKICAgICAgICAgICAgICAgaG92ZXJ0ZXh0ID0gfnBhc3RlKCJHZW5kZXI6ICIsIGdlbmRlcl9pZGVudGl0eSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjPGJyPiBpcyBIVE1MIGNvZGUgZm9yIGEgbGluZSBicmVhawogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzcHJpbnRmIC0gdXNlZCB0byBmb3JtYXQgc3RyaW5ncwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+UGVyY2VudGFnZTogIiwgc3ByaW50ZigiJS4yZiUlIiwgcGVyY2VudGFnZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+T2JzZXJ2YXRpb25zOiAiLCBvYnNlcnZhdGlvbiksCiAgICAgICAgICAgICAgICMgdGVsbHMgcGxvdGx5IHRvIG9ubHkgZGlzcGxheSB0aGUgdGV4dCBwcm92aWRlZCBpbiBob3ZlcnRleHQKICAgICAgICAgICAgICAgaG92ZXJpbmZvID0gJ3RleHQnLAogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gJ3JnYigxNTgsMjAyLDIyNSknLCBsaW5lID0gbGlzdChjb2xvciA9ICdyZ2IoOCw0OCwxMDcpJywgd2lkdGggPSAxLjUpKSwKICAgICAgICAgICAgICAgd2lkdGggPSA4MDAsIGhlaWdodCA9IDYwMCkKCiMgQXBwbHkgYSBsb2cgc2NhbGUgdG8gdGhlIHktYXhpcwpuZXdfZmlnIDwtIGxheW91dChuZXdfZmlnLAogICAgICAgICAgICAgIHRpdGxlID0gJ1BlcmNlbnRhZ2Ugb2YgRWFjaCBHZW5kZXIgSWRlbnRpdHkgaW4gRW5nbGFuZCBhbmQgV2FsZXMnLAogICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdHZW5kZXIgSWRlbnRpdHknLCBzaG93bGluZSA9IFRSVUUpLAogICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0eXBlID0gJ2xvZycsIHRpdGxlID0gJ1BlcmNlbnRhZ2UgKExvZyBTY2FsZSknKSkKCmBgYAoKYGBge3J9Cm5ld19maWcKYGBgCgoKCgojIERhdGFzZXQgMgoKVGhpcyBkYXRhc2V0IGNsYXNzaWZpZXMgcmVzaWRlbnRzIGJ5IGdlbmRlciBpZGVudGl0eSBhbmQgYWdlLCB3aXRoIHRoZSB1bml0IG9mIGFuYWx5c2lzIGJlaW5nIEVuZ2xhbmQgYW5kIFdhbGVzLiAKCmBgYHtyfQojIExvYWQgaW4gZGF0YXNldCAKCmRmMiA8LSByZWFkX2NzdignRGF0YS9HSV9hZ2UuY3N2JykKYGBgCgoKYGBge3J9CiMgQnJpZWYgZ2xpbXBzZSBvZiBkYXRhIHN0cnVjdHVyZQpoZWFkKGRmMiwgMTApCmBgYAoKYGBge3J9CiMgTGV0J3MgY2hlY2sgb3V0IHRoZSBkaW1lbnNpb25zCgpkaW0oZGYyKQpgYGAKCiMjIERhdGEgQ2xlYW5pbmcKCiogQ2xlYW4gY29sdW1uIG5hbWVzCiogRmlsdGVyIG91dCB1bmVjZXNzYXJ5IGNhdGVnb3JpZXMKKiBDbGVhbiBnZW5kZXIgaWRlbnRpdHkgY2F0ZWdvcnkgdmFsdWVzIC0gdG9vIHdvcmR5CiogRW5zdXJlIGdlbmRlcl9pZGVudGl0eSBjb2x1bW4gaXMgYSBmYWN0b3Igd2l0aCBsZXZlbHMgaW4gZGVzaXJlZCBvcmRlcgoqIENsZWFuIGFnZSBjYXRlZ29yeSB2YWx1ZXMgLSB0b28gd29yZHkKCldlJ2xsIHdoaXogdGhyb3VnaCB0aGlzLCBiZWNhdXNlIGl0J3MgdGhlIHNhbWUgc3R1ZmYgd2UgZGlkIGZvciB0aGUgbGFzdCBkYXRhc2V0LiAKCmBgYHtyfQojIHN0cl9yZXBsYWNlX2FsbCgpIG1ldGhvZCBmaW5kcyBhbGwgc3Vic3RyaW5ncyB3aGljaCBtYXRjaCB0aGUgcmVnZXggYW5kIHJlcGxhY2VzIHRoZW0gd2l0aCBlbXB0eSBzdHJpbmcKIyBGaXJzdCwgbGV0J3MgcmVwbGFjZSBhbnkgYnJhY2tldHMgd2l0aCBlbXB0eSBzdHJpbmdzCmNvbG5hbWVzKGRmMikgPC0gc3RyX3JlcGxhY2VfYWxsKGNvbG5hbWVzKGRmMiksICJcXHMqXFwoW14pXSpcXCkiLCAiIikKCiMgTG93ZXJjYXNlIGNvbHVtbiB0ZXh0IGFuZCByZXBsYWNlIGVtcHR5IHNwYWNlcyB3aXRoICJfIgpjb2xuYW1lcyhkZjIpIDwtIHRvbG93ZXIoY29sbmFtZXMoZGYyKSkKY29sbmFtZXMoZGYyKSA8LSBzdHJfcmVwbGFjZV9hbGwoY29sbmFtZXMoZGYyKSwgIiAiLCAiXyIpCgojIExldCdzIHNlZSBpZiBpdCB3b3JrZWQuLgpjb2xuYW1lcyhkZjIpCgpgYGAKCmBgYHtyfQojIEdldCByaWQgb2YgY29sdW1ucyB0aGF0IGRvIG5vdCBhcHBseQpkZjIgPC0gZGYyICU+JSAKICBmaWx0ZXIoZ2VuZGVyX2lkZW50aXR5X2NvZGUgIT0gLTgpIAoKIyBVc2UgdW5pcXVlIGFuZCBhY2Nlc3MgY29sdW1uIHRvIG91dHB1dCBpdHMgdW5pcXVlIHZhbHVlcwoKdW5pcXVlKGRmMiRnZW5kZXJfaWRlbnRpdHlfY29kZSkKYGBgCgpgYGB7cn0KIyBHZXQgcmlkIG9mIHJlZHVuZGFudCBhZ2UgY2F0ZWdvcnkKIyBGdXJ0aGVyIGZpbHRlciBkYXRhCmRmMiA8LSBkZjIgJT4lCiAgZmlsdGVyKGFnZV9jb2RlICE9IDEpCgpgYGAKCmBgYHtyfQojIENsZWFuIHVwIHRoZSB2YWx1ZXMgaW4gdGhlICdhZ2UnIGNvbHVtbi4gTGV0J3Mgc2hvcnRlbiB0aGVtLgoKIyBDaGFpbiBzdHJfcmVwbGFjZSgpIGNhbGxzIHRvZ2V0aGVyIHRvIGFwcGx5IG11bHRpcGxlIHN0cmluZyByZXBsYWNlbWVudHMgaW4gc3VjY2Vzc2lvbgojIEVhY2ggc3RyX3JlcGxhY2UoKSBjYWxsIGlzIGFwcGxpZWQgdG8gdGhlIHJlc3VsdCBvZiB0aGUgcHJldmlvdXMgb25lCmRmMiRhZ2UgPC0gZGYyJGFnZSAlPiUKICBzdHJfcmVwbGFjZSgnQWdlZCAnLCAnJykgJT4lCiAgc3RyX3JlcGxhY2UoJ3RvJywgJy0nKSAlPiUKICBzdHJfcmVwbGFjZSgneWVhcnMnLCAnJykgJT4lCiAgc3RyX3JlcGxhY2UoJ2FuZCBvdmVyJywgJysnKSAlPiUKICBzdHJfcmVwbGFjZSgnIC0gJywgJy0nKQoKIyBXZSBjYW4gcGFzcyBvdXIgZGYgdG8gdGhlIHNlbGVjdCBmdW5jdGlvbiwgd2hlcmUgd2Ugc3BlY2lmeSB0aGUgY29sdW1uIHdlJ3JlIGludGVyZXN0ZWQgaW4uCiMgVGhlbiwgd2UgcGlwZSB0aGUgb3V0cHV0IHRvIHRoZSBoZWFkIGZ1bmN0aW9uLgpkZjIgJT4lCiAgc2VsZWN0KGFnZSkgJT4lCiAgaGVhZCgpCmBgYAoKYGBge3J9CiMgVXNlIGNvbWJvIG9mIG11dGF0ZSBhbmQgcmVjb2RlIHRvIHJlcGxhY2UgbXVsdGlwbGUgdmFsdWVzIGluIGNvbHVtbgojIC5kZWZhdWx0IGVuc3VyZXMgdGhhdCBhbnkgdmFsdWUgbm90IG1hdGNoaW5nIHRob3NlIHNwZWNpZmllZCBhcmUgbGVmdCB1bmNoYW5nZWQKCmRmMiA8LSBkZjIgJT4lCiAgbXV0YXRlKGdlbmRlcl9pZGVudGl0eSA9IHJlY29kZShnZW5kZXJfaWRlbnRpdHksCiAgICAiR2VuZGVyIGlkZW50aXR5IHRoZSBzYW1lIGFzIHNleCByZWdpc3RlcmVkIGF0IGJpcnRoIiA9ICJDaXNnZW5kZXIiLAogICAgIkdlbmRlciBpZGVudGl0eSBkaWZmZXJlbnQgZnJvbSBzZXggcmVnaXN0ZXJlZCBhdCBiaXJ0aCBidXQgbm8gc3BlY2lmaWMgaWRlbnRpdHkgZ2l2ZW4iID0gIkdlbmRlciBpZGVudGl0eSBkaWZmZXJlbnQgZnJvbSBzZXgiLAogICAgLmRlZmF1bHQgPSBnZW5kZXJfaWRlbnRpdHkpKQoKCmBgYAoKCmBgYHtyfQoKdW5pcXVlKGRmMiRnZW5kZXJfaWRlbnRpdHkpCmBgYAoKYGBge3J9CiMgV2UgdXNlIGZhY3RvciB0byBjb252ZXJ0IGdlbmRlcl9pZGVudGl0eSBjb2x1bW4gdG8gYSBmYWN0b3Igd2l0aCBzcGVjaWZpZWQgbGV2ZWxzIAojIFRoaXMgdGVsbHMgUGxvdGx5IHRoZSBleGFjdCBvcmRlciBpbiB3aGljaCB0byBkaXNwbGF5IGNhdGVnb3JpZXMKCmRmMiRnZW5kZXJfaWRlbnRpdHkgPC0gZmFjdG9yKGRmMiRnZW5kZXJfaWRlbnRpdHksIGxldmVscyA9IGMoCiAgIkNpc2dlbmRlciIsCiAgIkdlbmRlciBpZGVudGl0eSBkaWZmZXJlbnQgZnJvbSBzZXgiLAogICJUcmFucyB3b21hbiIsCiAgIlRyYW5zIG1hbiIsCiAgIkFsbCBvdGhlciBnZW5kZXIgaWRlbnRpdGllcyIsCiAgIk5vdCBhbnN3ZXJlZCIKKSkKYGBgCgoKIyMgUXVlc3Rpb24KCkhvdyBpcyBnZW5kZXIgaWRlbnRpdHkgZGlzdHJpYnV0ZWQgYW1vbmcgZGlmZmVyZW50IGFnZSBncm91cHM/CgpTb21lIHN1YnF1ZXN0aW9ucyB0aGF0IHRoaXMgY2FuIGhlbHAgdXMgYW5zd2VyOgoKKiBXaGF0ICUgb2YgdHJhbnMgd29tZW4gYXJlIGFnZWQgMTYtMjQgeWVhcnM/CiogQXJlIG9sZGVyIGFnZSBncm91cHMgb3ZlciByZXByZXNlbnRlZCBpbiB0aGUgJ25vbi1yZXNwb25zZScgY2F0ZWdvcnk/CgojIyBEYXRhIHByZS1wcm9jZXNzaW5nCgojIyMgQ2FsY3VsYXRlIHBlcmNlbnRhZ2VzIAoKQmVsb3csIHdlIHVzZSB0aGUgZ3JvdXBfYnkgZnVuY3Rpb24gdG8gZ3JvdXAgdGhlIGRhdGEgYnkgJ2dlbmRlcl9pZGVudGl0eScgYW5kIGNhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSB3aXRoaW4gZWFjaCBncm91cC4gVGhlbiB0aGUgbXV0YXRlKCkgZnVuY3Rpb24gYWRkcyBhIG5ldyBjb2x1bW4gJ3BlcmNlbnRhZ2UnIHRvIGRmLCB3aGljaCAoZm9yIGVhY2ggZ3JvdXApIGRpdmlkZXMgdGhlIG9ic2VydmF0aW9uIGJ5IHRoZSBzdW0gb2Ygb2JzZXJ2YXRpb25zLCBtdWx0aXBsaWVzIGl0IGJ5IDEwMCwgYW5kIHJvdW5kcyBpdCB1cCB0byAyIGRlY2ltYWwgcG9pbnRzLiBXZSB0aGVuIHVzZSB0aGUgdW5ncm91cCBmdW5jdGlvbiB3aGVuIHdlJ3JlIGRvbmUgd2l0aCB0aGUgZ3JvdXBpbmcgb3BlcmF0aW9uLiAKCmBgYHtyfQpkZjIgPC0gZGYyICU+JQogIGdyb3VwX2J5KGdlbmRlcl9pZGVudGl0eSkgJT4lCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSByb3VuZCgob2JzZXJ2YXRpb24gLyBzdW0ob2JzZXJ2YXRpb24pICogMTAwKSwgMikpICU+JQogIHVuZ3JvdXAoKQoKaGVhZChkZjIpCmBgYAoKCiMjIEludGVyYWN0aXZlIGdyb3VwZWQgYmFyIGNoYXJ0CgpXaGVuIGNyZWF0aW5nIGdyb3VwZWQgYmFyIGNoYXJ0cywgdGhlcmUncyBhIGZldyBzdWJ0bGUgZGlmZmVyZW5jZXMgdGhhdCB5b3UnbGwgbmVlZCB0byBhY2NvdW50IGZvciBpbiB0aGUgY29kZS4KRmlyc3QsIHdlJ2xsIG5lZWQgdG8gbWFrZSBzdXJlIHRoaXMgaXMgYSBncm91cGVkIGJhciBjaGFydCwgd2hpY2ggd2UgY2FuIHNldCB3aXRoIHRoZSAnYmFybW9kZScgcGFyYW1ldGVyLgpTZWNvbmQsIHdlJ2xsIG5lZWQgYSB3YXkgdG8gY29sb3VyIGVhY2ggYmFyIGluIGVhY2ggZ3JvdXAsIGFjY29yZGluZyB0byBhZ2UgY2F0ZWdvcmllcywgd2hpY2ggd2UgY2FuIGRvIHdpdGggdGhlICdjb2xvcicgYW5kICdjb2xvcnMnIHBhcmFtZXRlcnMuCgpgYGB7cn0KIyBDcmVhdGUgYSBncm91cGVkIGJhciBjaGFydCB3aXRoIGhvdmVyIGluZm9ybWF0aW9uCmZpZzIgPC0gcGxvdF9seShkYXRhID0gZGYyLCB4ID0gfmdlbmRlcl9pZGVudGl0eSwgeSA9IH5wZXJjZW50YWdlLCB0eXBlID0gJ2JhcicsCiAgICAgICAgICAgICAgICMgY29sb3Igc3BlY2lmaWVzIHdoaWNoIHZhcmlhYmxlIHRvIGNvbG91ciBieQogICAgICAgICAgICAgICAjIGNvbG9ycyBzcGVjaWZpZXMgdGhlIGNvbG91ciBwYWxldHRlIHRvIHVzZSwgYW5kIGhvdyBtYW55IGNvbG91cnMgYXJlIHJlcXVpcmVkCiAgICAgICAgICAgICAgIGNvbG9yID0gfmFnZSwgY29sb3JzID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKGxlbmd0aCh1bmlxdWUoZGYyJGFnZSkpLCAiU2V0MiIpLAogICAgICAgICAgICAgICBob3ZlcmluZm8gPSAndGV4dCcsCiAgICAgICAgICAgICAgIGhvdmVydGV4dCA9IH5wYXN0ZSgiT2JzZXJ2YXRpb246ICIsIG9ic2VydmF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5QZXJjZW50YWdlOiAiLCBzcHJpbnRmKCIlLjJmJSUiLCBwZXJjZW50YWdlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+QWdlIGdyb3VwOiAiLCBhZ2UpLAogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGxpbmUgPSBsaXN0KGNvbG9yID0gJ3JnYmEoMjU1LDI1NSwyNTUsIDAuNSknLCB3aWR0aCA9IDAuNSkpLAogICAgICAgICAgICAgICB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQogICAgICAgICAgICAgICAKYGBgCgpgYGB7cn0KZmlnMgpgYGAKCmBgYHtyfQpmaWcyIDwtIGxheW91dChmaWcyLHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBHZW5kZXIgSWRlbnRpdHkgQ2F0ZWdvcmllcyBBbW9uZyBBZ2UgR3JvdXBzJywKICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ0dlbmRlciBJZGVudGl0eScpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnUGVyY2VudGFnZScpLAogICAgICAgICBsZWdlbmQgPSBsaXN0KHRpdGxlID0gbGlzdCh0ZXh0ID0gJ0FnZSBHcm91cCcpKSkKCmBgYAoKYGBge3J9CmZpZzIKYGBgCgojIyBTdGFja2VkIGJhciBjaGFydAoKVGhlIG1ldGhvZCBJIHNob3cgYmVsb3cgc2ltcGx5IGNvbnZlcnRzIHRoZSBwcmV2aW91c2x5IG1hZGUgZ3JvdXBlZCBiYXIgY2hhcnQgJ2ZpZzInIHRvIGEgc3RhY2tlZCBiYXIgY2hhcnQuIFN0YWNrZWQgYmFyIGNoYXJ0cyBjYW4gb25seSBiZSBjcmVhdGVkIHVzaW5nIHRoZSBsYXlvdXQoKSBmdW5jdGlvbiB0byBjaGFuZ2UgdGhlIGJhcm1vZGUsIGFzIHRoZSBkZWZhdWx0IGlzIGEgZ3JvdXBlZCBiYXIgY2hhcnQuIAoKCgpgYGB7cn0KIyBDb252ZXJ0IHRvIHN0YWNrZWQgYmFyIGNoYXJ0CgpzdF9maWcgPC0gbGF5b3V0KGZpZzIsCiAgICAgICAgIGJhcm1vZGUgPSAnc3RhY2snKQoKc3RfZmlnCmBgYAoKCgojIyBEYXRhc2V0IDMKClRoaXMgZGF0YXNldCBjbGFzc2lmaWVzIHJlc2lkZW50cyBieSBnZW5kZXIgaWRlbnRpdHkgYW5kIGV0aG5pYyBncm91cCwgd2l0aCB0aGUgdW5pdCBvZiBhbmFseXNpcyBiZWluZyB0aGUgMzMxIGxvY2FsIGF1dGhvcml0aWVzIGFjcm9zcyBFbmdsYW5kIGFuZCBXYWxlcy4gCgpgYGB7cn0KIyBMb2FkIGluIGRhdGFzZXQKCmRmMyA8LSByZWFkX2NzdignRGF0YS9HSV9ldGhuaWMuY3N2JykKYGBgCgoKYGBge3J9CiMgQnJpZWYgZ2xpbXBzZSBhdCB1bmRlcmx5aW5nIGRhdGEgc3RydWN0dXJlCmhlYWQoZGYzLCAxMCkKYGBgCgojIyBEYXRhIENsZWFuaW5nCgoqIENsZWFuIGNvbHVtbiBuYW1lcwoqIEZpbHRlciBvdXQgdW5uZWNlc3NhcnkgY2F0ZWdvcmllcwoKQmVsb3csIEkgcHJvdmlkZSBhbm90aGVyIG1ldGhvZCAnZ3N1YigpJyB3aGljaCBjYW4gYmUgdXNlZCBpbnN0ZWFkIG9mIHRoZSBzdHJfcmVwbGFjZV9hbGwoKSBtZXRob2Qgd2hpY2ggaGFzIGJlZW4gZGVtb25zdHJhdGVkIGluIHRoZSBwcmV2aW91cyBjbGVhbmluZyBzZWN0aW9ucy4gQmFzaWNhbGx5LCBsb29rcyBmb3IgYSBwYXR0ZXJuIGFuZCBhcHBsaWVzIHRoZSByZXBsYWNlbWVudCB0byBhbnkgY29sdW1uIG5hbWVzIHdoaWNoIG1hdGNoIHRoZSBwYXR0ZXJuLiAKCmBgYHtyfQojIFJlbW92ZSBhbGwgdGV4dCB3aXRoaW4gcGFyZW50aGVzZXMgZnJvbSBjb2x1bW4gbmFtZXMgYW5kIHJlcGxhY2UgaXQgd2l0aCBhbiBlbXB0eSBzdHJpbmcKCiMgdGlsZGUgb3BlcmF0b3IgKH4pIHVzZWQgdG8gYXBwbHkgZnVuY3Rpb24gJ2dzdWInIHRvIGVhY2ggY29sbmFtZQojIC54IHJlcHJlc2VudHMgZWFjaCBjb2xuYW1lIHRoYXQgZ3N1YiB3aWxsIGJlIGFwcGxpZWQgdG8KZGYzIDwtIGRmMyAlPiUgCiAgcmVuYW1lX3dpdGgofiBnc3ViKCJcXHMqXFwoW14pXSpcXCkiLCAiIiwgLngpKQpgYGAKCmBgYHtyfQojIExvd2VyY2FzZSBhbGwgdGV4dCBpbiBjb2x1bW4gbmFtZXMgYW5kIHJlcGxhY2Ugc3BhY2VzIHdpdGggdW5kZXJzY29yZXMKZGYzIDwtIGRmMyAlPiUgCiAgcmVuYW1lX3dpdGgofiB0b2xvd2VyKGdzdWIoIiAiLCAiXyIsIC54KSkpCmBgYAoKYGBge3J9CiMgU2hvcnRlbiB0aGUgbG9jYWwgYXV0aG9yaXR5IGNvbHVtbiBuYW1lcyBhcyB0aGV5IGFyZSB3YXkgdG9vIGxvbmcKZGYzIDwtIGRmMyAlPiUgCiAgcmVuYW1lKExBX2NvZGUgPSBsb3dlcl90aWVyX2xvY2FsX2F1dGhvcml0aWVzX2NvZGUsCiAgICAgICAgIExBX25hbWUgPSBsb3dlcl90aWVyX2xvY2FsX2F1dGhvcml0aWVzKQoKYGBgCgpgYGB7cn0KIyBMZXQncyBzZWUgaWYgaXQgd29ya2VkCmNvbG5hbWVzKGRmMykKYGBgCgoKYGBge3J9CiMgUmVtb3ZlICdEb2VzIG5vdCBhcHBseScgY2F0ZWdvcmllcyBmb3IgdGhlIGdlbmRlciBpZGVudGl0eSBhbmQgZXRobmljIGdyb3VwIGNvbHVtbnMKZGYzIDwtIGRmMyAlPiUgCiAgZmlsdGVyKGdlbmRlcl9pZGVudGl0eV9jb2RlICE9IC04LCBldGhuaWNfZ3JvdXBfY29kZSAhPSAtOCkKYGBgCgpgYGB7cn0KIyBMZXQncyBzZWUgaWYgaXQgd29ya2VkLi4gCnVuaXF1ZShkZjMkZ2VuZGVyX2lkZW50aXR5X2NvZGUpCmBgYAoKYGBge3J9CiMgTGV0J3Mgc2VlIGlmIGl0IHdvcmtlZC4uCnVuaXF1ZShkZjMkZXRobmljX2dyb3VwX2NvZGUpCmBgYAoKIyMgUXVlc3Rpb24KCkhvdyBkb2VzIHRoZSByYXRlIG9mICdub24tcmVzcG9uc2UnIG9uIGdlbmRlciBpZGVudGl0eSB2YXJ5IGFtb25nIGRpZmZlcmVudCBldGhuaWMgZ3JvdXBzIGFjcm9zcyBsb2NhbCBhdXRob3JpdGllcyBpbiBFbmdsYW5kIGFuZCBXYWxlcz8KCkEgc3VicXVlc3Rpb24gdGhpcyBjb3VsZCBoZWxwIHVzIGFuc3dlcjoKCkRvZXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG5vbi1yZXNwb25zZSBhbmQgZXRobmljIGdyb3VwICUgZm9yIGxvY2FsIGF1dGhvcml0aWVzIGRpZmZlciBiZXR3ZWVuIHRoZSAnV2hpdGUnIGNhdGVnb3JpZXMgYW5kIG90aGVyIGV0aG5pYyBncm91cHM/CgojIyBEYXRhIHByZS1wcm9jZXNzaW5nCgpHaXZlbiB0aGF0IEkgd2FudCB0byBleHBsb3JlIHRoZSBxdWVzdGlvbiBhYm92ZSwgSSdkIGxpa2UgdG8gY3JlYXRlIGEgc2NhdHRlcnBsb3Qgd2hpY2ggZXhwbG9yZXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSAlIG9mIGNlcnRhaW4gZXRobmljIGdyb3VwcyB3aXRoaW4gbG9jYWwgYXV0aG9yaXRpZXMgYW5kIHRoZWlyIG5vbi1yZXNwb25zZSByYXRlcy4gVGhlcmVmb3JlLCBJJ2xsIG5lZWQgdG8gcHJlcCBteSB4IGFuZCB5IHZhcmlhYmxlcywgc28gSSdsbCBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSBvZiBlYWNoIGV0aG5pYyBncm91cCBpbiBlYWNoIExBLCBhbmQgdGhhdCBldGhuaWMgZ3JvdXBzIG5vbi1yZXNwb25zZSByYXRlIHdpdGhpbiBlYWNoIExBLiAgCgojIyMgQ2FsY3VsYXRlICUgb2YgZWFjaCBldGhuaWMgZ3JvdXAgaW4gZWFjaCBMQQoKYGBge3J9CiMgRmlyc3QsIHdlJ3JlIGdvaW5nIHRvIGdyb3VwIG91ciBkYXRhIGJ5IExBX25hbWUsIGV0aG5pYyBncm91cCwgYW5kIHN1bSBvdXIgb2JzZXJ2YXRpb25zCiMgVGhpcyBsZWF2ZXMgdXMgd2l0aCB0aGUgdG90YWwgb2YgZWFjaCBldGhuaWMgZ3JvdXAgaW4gZWFjaCBsb2NhbCBhdXRob3JpdHkKZXRobmljX3RvdGFscyA8LSBkZjMgJT4lCiAgZ3JvdXBfYnkoTEFfbmFtZSwgZXRobmljX2dyb3VwKSAlPiUKICBzdW1tYXJpc2UoRXRobmljX3N1bSA9IHN1bShvYnNlcnZhdGlvbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCgojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyB0byBjaGVjawpoZWFkKGV0aG5pY190b3RhbHMpCmBgYAoKCmBgYHtyfQojIENhbGN1bGF0ZSB0b3RhbCBvYnNlcnZhdGlvbnMgZm9yIGVhY2ggbG9jYWwgYXV0aG9yaXR5IGJ5IGdyb3VwaW5nIGRmMyBieSBsb2NhbCBhdXRob3JpdHkgYW5kIHN1bW1pbmcgdXAgb2JzCmxhX3RvdGFscyA8LSBkZjMgJT4lCiAgZ3JvdXBfYnkoTEFfbmFtZSkgJT4lCiAgc3VtbWFyaXNlKExBX3N1bSA9IHN1bShvYnNlcnZhdGlvbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCgojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyB0byBjaGVjawpoZWFkKGxhX3RvdGFscykKYGBgCgpgYGB7cn0KIyBNZXJnZSB0aGUgZXRobmljX3RvdGFscyBhbmQgbGFfdG90YWxzIGRhdGFmcmFtZXMgdG9nZXRoZXIKIyBieSBwYXJhbWV0ZXIgc3BlY2lmaWVzIHdoaWNoIGNvbHVtbiB0byBwZXJmb3JtIG1lcmdlIG9uCgpncnBfcGN0IDwtIG1lcmdlKGV0aG5pY190b3RhbHMsIGxhX3RvdGFscywgYnkgPSAiTEFfbmFtZSIpCmBgYAoKYGBge3J9CiMgQ2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIGVhY2ggZXRobmljIGdyb3VwIHdpdGhpbiBlYWNoIGxvY2FsIGF1dGhvcml0eQojIFN0b3JlIHJlc3VsdHMgaW4gbmV3IGNvbHVtbiAKCmdycF9wY3QgPC0gZ3JwX3BjdCAlPiUKICBtdXRhdGUoUGVyY2VudGFnZSA9IHJvdW5kKChFdGhuaWNfc3VtIC8gTEFfc3VtICogMTAwKSwgMikpCmBgYAoKCmBgYHtyfQojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyB0byBjaGVjawpoZWFkKGdycF9wY3QsIDEwKQpgYGAKCiMjIyBDYWxjdWxhdGUgRXRobmljIEdyb3VwIE5vbi1SZXNwb25zZSBSYXRlcyAoJSdzKSBXaXRoaW4gTEFzCgpgYGB7cn0KIyBXZSBhbHJlYWR5IGhhdmUgb3VyIGV0aG5pYyBncm91cCB0b3RhbHMgd2hpY2ggd2UgY2FuIHJlLXVzZS4uLgoKZXRobmljX3RvdGFscwpgYGAKCmBgYHtyfQojIENhbGN1bGF0ZSBzdW0gb2Ygbm9uLXJlc3BvbnNlcyBmb3IgZWFjaCBldGhuaWMgZ3JvdXAgd2l0aGluIGVhY2ggTEEKIyBGaWx0ZXIgZGYzIHNvIHRoYXQgd2Ugb25seSBoYXZlIG5vbi1yZXNwb25zZSByb3dzCiMgR3JvdXAgYnkgTEEgYW5kIGV0aG5pYyBncm91cCB0aGVuIHN1bSBub24tcmVzcG9uc2Ugb2JzIGFuZCBzdG9yZSB0aGUgcmVzdWx0cyBpbiBuZXcgY29sdW1uCgpub25fcmVzcG9uc2VfdG90YWxzIDwtIGRmMyAlPiUKICBmaWx0ZXIoZ2VuZGVyX2lkZW50aXR5ID09ICdOb3QgYW5zd2VyZWQnKSAlPiUKICBncm91cF9ieShMQV9uYW1lLCBldGhuaWNfZ3JvdXApICU+JQogIHN1bW1hcmlzZShOUl90b3RhbCA9IHN1bShvYnNlcnZhdGlvbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCmBgYAoKYGBge3J9CgojIExldCdzIGNoZWNrIGl0IG91dC4uIApoZWFkKG5vbl9yZXNwb25zZV90b3RhbHMpCmBgYAoKCmBgYHtyfQojIE1lcmdlIHRoZSB0ZXRobmljIGdyb3VwIHRvdGFscyB3aXRoIHRoZSBldGhuaWMgZ3JvdXAgbm9uLXJlc3BvbnNlIHRvdGFscwojIGMgLSB1c2VkIHdoZW4gd2UncmUgcmVmZXJlbmNpbmcgbW9yZSB0aGFuIG9uZSBjb2x1bW4KIyBhbGwueCAtIHBlcmZvcm1zIGEgbGVmdCBqb2luCmdycF9uciA8LSBtZXJnZShldGhuaWNfdG90YWxzLCBub25fcmVzcG9uc2VfdG90YWxzLCBieSA9IGMoIkxBX25hbWUiLCAiZXRobmljX2dyb3VwIiksIGFsbC54ID0gVFJVRSkKCmBgYAoKYGBge3J9CiMgTGV0J3MgY2hlY2sgaXQgb3V0Li4gCgpoZWFkKGdycF9ucikKYGBgCgoKYGBge3J9CiMgQ2FsY3VsYXRlIHRoZSBub24tcmVzcG9uc2UgcGVyY2VudGFnZSBmb3IgZWFjaCBldGhuaWMgZ3JvdXAgd2l0aGluIGVhY2ggTEEKIyBTdG9yZSByZXN1bHRzIGluIG5ldyBjb2x1bW4KCmdycF9uciA8LSBncnBfbnIgJT4lCiAgbXV0YXRlKEV0aF9OUl9QZXJjID0gcm91bmQoKE5SX3RvdGFsIC8gRXRobmljX3N1bSAqIDEwMCksIDIpKQpgYGAKCgpgYGB7cn0KIyBRdWljayBnbGFuY2UuLiAKaGVhZChncnBfbnIpCmBgYAojIyMgTWVyZ2UgYm90aCBkYXRhc2V0cwoKTm93IHRoYXQgd2UndmUgY29tcGxldGVkIHRoZSBuZWNlc3NhcnkgY2FsY3VsYXRpb25zLCB3ZSBhcmUgbGVmdCB3aXRoIHR3byBkYXRhc2V0czoKCiogZ3JwX3BjdCAtIGRldGFpbHMgdGhlICUgb2YgZWFjaCBldGhuaWNfZ3JvdXAgaW4gZWFjaCBMQQoqIGdycF9uciAtIGRldGFpbHMgdGhlIGV0aG5pYyBncm91cCBub24tcmVzcG9uc2UgJSBpbiBlYWNoIExBCgpBbGwgd2UgbmVlZCB0byBkbyBub3cgdGhlbiwgaXMgbWVyZ2UgdGhlc2UgZGF0YXNldHMgdG9nZXRoZXIgc28gdGhhdCB3ZSBjYW4gYWNjZXNzIHRoZSBuZXcgY29sdW1ucyBhbmQgcGxvdCB0aGVtOgoKKiBQZXJjZW50YWdlCiogRXRoX05SX1BlcmMKCmBgYHtyfQojIE1lcmdlIHRoZSBub24tcmVzcG9uc2UgZGF0YSB3aXRoIHRoZSBwZXJjZW50YWdlIG9mIGVhY2ggZXRobmljIGdyb3VwIHdpdGhpbiBlYWNoIExBCiMgVXNlIHNlbGVjdCB0byBpc29sYXRlIGNvbHVtbnMgSSB3YW50IHRvIHByZXNlcnZlIGluIHRoZSBtZXJnZSwgTEFfc3VtIGlzIHJlZHVuZGFudC4uLgoKbnIgPC0gbWVyZ2UoZ3JwX25yLCBzZWxlY3QoZ3JwX3BjdCwgTEFfbmFtZSwgZXRobmljX2dyb3VwLCBQZXJjZW50YWdlKSwgYnkgPSBjKCJMQV9uYW1lIiwgImV0aG5pY19ncm91cCIpKQpgYGAKCmBgYHtyfQojIFF1aWNrIGdsYW5jZQoKaGVhZChucikKYGBgCgoKIyMgSW50ZXJhY3RpdmUgc2NhdHRlcnBsb3QKCkluIHRoaXMgc2VjdGlvbiB3ZSdyZSBnb2luZyB0bzoKCjEuIENyZWF0ZSBhIHNpbXBsZSBzY2F0dGVycGxvdCBleHBsb3JpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBwZXJjZW50YWdlIG9mIGFzaWFuIGNpdGl6ZW5zIHdpdGhpbiBsb2NhbCBhdXRob3JpdGllcyBhbmQgdGhlaXIgbm9uLXJlc3BvbnNlIHJhdGVzCgoyLiBJbXBsZW1lbnQgYSBkcm9wZG93biB3aWRnZXQgdG8gdXBkYXRlIG91ciBzY2F0dGVycGxvdCAKCmBgYHtyfQojIFN1YnNldCBkYXRhZnJhbWUgc28gd2Ugb25seSBoYXZlIHJlc3BvbnNlcyBmcm9tIHRoZSBhc2lhbiBldGhuaWMgZ3JvdXAKCmFzaWFuIDwtIG5yICU+JQogIGZpbHRlcihldGhuaWNfZ3JvdXAgPT0gJ0FzaWFuLCBBc2lhbiBCcml0aXNoIG9yIEFzaWFuIFdlbHNoJykKYGBgCgoKYGBge3J9CiMgQ2hlY2sgaXQgb3V0Li4gCgpoZWFkKGFzaWFuKQpgYGAKCmBgYHtyfQojIEluaXRpYWxpemUgZmlndXJlCmZpZzMgPC0gcGxvdF9seShkYXRhID0gYXNpYW4sCiAgICAgICAgICAgICAgIHggPSB+UGVyY2VudGFnZSwKICAgICAgICAgICAgICAgeSA9IH5FdGhfTlJfUGVyYywKICAgICAgICAgICAgICAgdGV4dCA9IH5wYXN0ZSgnTEEgTmFtZTonLCBMQV9uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+Tm9uLXJlc3BvbnNlIFRvdGFsOicsIE5SX3RvdGFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+RXRobmljIEdyb3VwIFRvdGFsOicsIEV0aG5pY19zdW0pLAogICAgICAgICAgICAgICBob3ZlcmluZm8gPSAidGV4dCIsCiAgICAgICAgICAgICAgIG1vZGUgPSAnbWFya2VycycsICAjIFNwZWNpZnkgbWFya2VyIHBvaW50cwogICAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCAgIyBHcmFwaCB0eXBlIC0gc2NhdHRlcnBsb3QKICAgICAgICAgICAgICAgbmFtZSA9ICdBc2lhbicpICAjIERlZmF1bHQgdmlzaWJsZSBncmFwaAoKCiMgQ3VzdG9taXplIGxheW91dCAKZmlnMyA8LSBmaWczICU+JQogIGxheW91dCh0aXRsZSA9ICdOb24tUmVzcG9uc2UgUmF0ZXMgb2YgdGhlIEFzaWFuIEV0aG5pYyBHcm91cCBBY3Jvc3MgTG9jYWwgQXV0aG9yaXRpZXMnLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnUGVyY2VudGFnZSBvZiBFdGhuaWMgR3JvdXAnKSwKICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ05vbi1yZXNwb25zZSBSYXRlJyksCiAgICAgICAgIHdpZHRoID0gNzAwLAogICAgICAgICBoZWlnaHQgPSA3MDApCgojIFNob3cgdGhlIHBsb3QKZmlnMwpgYGAKCiMjIERyb3Bkb3duIHNlbGVjdGlvbgoKV2hhdCB3ZSdyZSBnb2luZyB0byBkbyBub3csIGlzIHVzZSBQbG90bHkncyAndXBkYXRlbWVudXMnIGluIGNvbmp1bmN0aW9uIHdpdGggdGhlICd1cGRhdGUnIG1ldGhvZCB0byBjcmVhdGUgYSBkcm9wZG93biB3aGVyZSB3ZSBjYW4gc3dpdGNoIGJldHdlZW4gdGhlIEFzaWFuIGV0aG5pYyBncm91cCwgYW5kIHRoZSBXaGl0ZSBldGhuaWMgZ3JvdXAgdG8gbWFrZSBzb21lIGNvbXBhcmlzb25zLiAKCiMjIyBTdGVwIDE6IEluaXRpYWxpc2UgZmlndXJlIGFuZCBhZGQgdHJhY2VzCgpXZSdsbCBzdGFydCBieSBjcmVhdGluZyBhIHBsb3RfbHkgZmlndXJlIHdpdGggbm8gZGF0YSBvciB2YXJpYWJsZXMgc3BlY2lmaWVkLiBUaGlzIGlzIGJlY2F1c2Ugd2UncmUgZ29pbmcgdG8gdXNlIGFkZF90cmFjZSB0byBhZGQgb3VyIHR3byBzZXRzIG9mIGRhdGFwb2ludHMgdG8gdGhlIHBsb3QuICdUcmFjZXMnIHJlZmVyIHRvIGEgc2V0IG9mIGRhdGEsIHNvIGluIG91ciBleGFtcGxlIHdlIHdhbnQgdG8gYWRkIGEgdHJhY2Ugd2l0aCB0aGUgZGF0YSBwb2ludHMgcmVsYXRpbmcgdG8gb3VyIGFzaWFuIGV0aG5pYyBncm91cCwgYW5kIGFub3RoZXIgb25lIGZvciBvdXIgd2hpdGUgZXRobmljIGdyb3VwLiBUaGlzIHdpbGwgc3RhcnQgdG8gbWFrZSBzZW5zZSB3aGVuIHdlIGxvb2sgYXQgdGhlIGNvZGUgYmVsb3cuIAoKYGBge3J9CiMgSW5pdGlhbGl6ZSBhIFBsb3RseSBmaWd1cmUKZmlnNCA8LSBwbG90X2x5KCkKCiMgTGV0J3MgdGFrZSBhIGxvb2suLgojIFRoaXMgaXMgb3VyIGJ1aWxkaW5nIGJsb2NrCmZpZzQKCmBgYAoKCgpgYGB7cn0KIyBTdWJzZXQgZGF0YWZyYW1lIHNvIHdlIG9ubHkgaGF2ZSByZXNwb25zZXMgZnJvbSB0aGUgd2hpdGUgZXRobmljIGdyb3VwCndoaXRlIDwtIG5yICU+JQogIGZpbHRlcihldGhuaWNfZ3JvdXAgPT0gJ1doaXRlOiBFbmdsaXNoLCBXZWxzaCwgU2NvdHRpc2gsIE5vcnRoZXJuIElyaXNoIG9yIEJyaXRpc2gnKQoKYGBgCgpgYGB7cn0KIyBRdWljayBjaGVjay4uLgpoZWFkKHdoaXRlKQpgYGAKCgpgYGB7cn0KIyBBZGQgdHJhY2UgZm9yIHRoZSBBc2lhbiBldGhuaWMgZ3JvdXAKCmZpZzQgPC0gZmlnNCAlPiUgYWRkX3RyYWNlKAogIGRhdGEgPSBhc2lhbiwKICB4ID0gflBlcmNlbnRhZ2UsCiAgeSA9IH5FdGhfTlJfUGVyYywKICB0ZXh0ID0gfnBhc3RlKCdMQSBOYW1lOicsIExBX25hbWUsCiAgICAgICAgICAgICAgICAnPGJyPk5vbi1yZXNwb25zZSBUb3RhbDonLCBOUl90b3RhbCwKICAgICAgICAgICAgICAgICc8YnI+RXRobmljIEdyb3VwIFRvdGFsOicsIEV0aG5pY19zdW0pLAogIHR5cGUgPSAnc2NhdHRlcicsCiAgbW9kZSA9ICdtYXJrZXJzJywKICBuYW1lID0gJ0FzaWFuJywKICBob3ZlcmluZm8gPSAndGV4dCcsCiAgdmlzaWJsZSA9IFQKKQoKIyBBZGQgdHJhY2UgZm9yIHRoZSBXaGl0ZSBldGhuaWMgZ3JvdXAKZmlnNCA8LSBmaWc0ICU+JSBhZGRfdHJhY2UoCiAgZGF0YSA9IHdoaXRlLAogIHggPSB+UGVyY2VudGFnZSwKICB5ID0gfkV0aF9OUl9QZXJjLAogIHRleHQgPSB+cGFzdGUoJ0xBIE5hbWU6JywgTEFfbmFtZSwKICAgICAgICAgICAgICAgICc8YnI+Tm9uLXJlc3BvbnNlIFRvdGFsOicsIE5SX3RvdGFsLAogICAgICAgICAgICAgICAgJzxicj5FdGhuaWMgR3JvdXAgVG90YWw6JywgRXRobmljX3N1bSksCiAgdHlwZSA9ICdzY2F0dGVyJywKICBtb2RlID0gJ21hcmtlcnMnLAogIG5hbWUgPSAnV2hpdGUnLAogIGhvdmVyaW5mbyA9ICd0ZXh0JywKICB2aXNpYmxlID0gRgopCgpmaWc0CmBgYAoKIyMjIFN0ZXAgMjogQ29uZmlndXJlIGRyb3Bkb3duIGJ1dHRvbnMgYW5kIHNldCBpbml0aWFsIHZpc2liaWxpdHkgb2YgZGF0YXNldHMKCmBgYHtyfQoKIyBEZWZpbmUgZHJvcGRvd24gYnV0dG9ucyBmb3IgaW50ZXJhY3Rpdml0eQpmaWc0IDwtIGZpZzQgJT4lIGxheW91dCgKICB0aXRsZSA9ICJOb24tUmVzcG9uc2UgUmF0ZXMgQWNyb3NzIExvY2FsIEF1dGhvcml0aWVzIiwKICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiUGVyY2VudGFnZSBvZiBFdGhuaWMgR3JvdXAiKSwKICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiTm9uLXJlc3BvbnNlIFJhdGUiKSwKICAjIEhpZGUgdGhlIGxlZ2VuZCwgYXMgaW50ZXJhY3RpdmUgZHJvcGRvd24gd2lsbCBoYW5kbGUgdHJhY2UgdmlzaWJpbGl0eQogIHNob3dsZWdlbmQgPSBGQUxTRSwKICAjIEFkZCBkcm9wZG93biBtZW51IGZvciBpbnRlcmFjdGl2ZSBwbG90IHVwZGF0ZXMKICB1cGRhdGVtZW51cyA9IGxpc3QoCiAgICBsaXN0KAogICAgICB0eXBlID0gImRyb3Bkb3duIiwKICAgICAgYnV0dG9ucyA9IGxpc3QoCiAgICAgICAgbGlzdCgKICAgICAgICAgICMgdGhlIHVwZGF0ZSBtZXRob2QgY2hhbmdlcyBwbG90IGF0dHJpYnV0ZXMgd2hlbiBhIGJ1dHRvbiBpcyBjbGlja2VkCiAgICAgICAgICBtZXRob2QgPSAidXBkYXRlIiwKICAgICAgICAgICMgRmlyc3QgYnV0dG9uIG1ha2VzIEFzaWFuIGRhdGEgdmlzaWJsZSBhbmQgaGlkZXMgdGhlIFdoaXRlIGRhdGEKICAgICAgICAgIGFyZ3MgPSBsaXN0KGxpc3QoInZpc2libGUiID0gbGlzdChUUlVFLCBGQUxTRSkpLAogICAgICAgICAgICAgICAgICAgICAgIyBVcGRhdGUgdGhlIHRpdGxlIHNwZWNpZmljIHRvIHRoZSBBc2lhbiBkYXRhCiAgICAgICAgICAgICAgICAgICAgICBsaXN0KCJ0aXRsZSIgPSAiTm9uLVJlc3BvbnNlIFJhdGVzIG9mIHRoZSBBc2lhbiBFdGhuaWMgR3JvdXAgQWNyb3NzIExvY2FsIEF1dGhvcml0aWVzIikpLAogICAgICAgICAgIyBTcGVjaWZ5IGJ1dHRvbiBsYWJlbAogICAgICAgICAgbGFiZWwgPSAiQXNpYW4iCiAgICAgICAgKSwKICAgICAgICBsaXN0KAogICAgICAgICAgbWV0aG9kID0gInVwZGF0ZSIsCiAgICAgICAgICBhcmdzID0gbGlzdChsaXN0KCJ2aXNpYmxlIiA9IGxpc3QoRkFMU0UsIFRSVUUpKSwKICAgICAgICAgICAgICAgICAgICAgIGxpc3QoInRpdGxlIiA9ICJOb24tUmVzcG9uc2UgUmF0ZXMgb2YgdGhlIFdoaXRlIEV0aG5pYyBHcm91cCBBY3Jvc3MgTG9jYWwgQXV0aG9yaXRpZXMiKSksCiAgICAgICAgICBsYWJlbCA9ICJXaGl0ZSIKICAgICAgICApCiAgICAgICkKICAgICkKICApCikKCiMgRGlzcGxheSB0aGUgZmlndXJlCmZpZzQKYGBgCgojIERhdGFzZXQgMwoKVGhpcyBkYXRhc2V0IGluY2x1ZGVzIHNleHVhbCBpZGVudGl0eSBlc3RpbWF0ZXMgYnkgZ2VuZGVyIGZyb20gMjAxMCB0byAyMDE0LiBUaGlzIGlzIHByZXNlbnRlZCBhdCBhIFVLIGxldmVsLCBhbmQgYnJva2VuIGRvd24gYnkgRW5nbGFuZCwgV2FsZXMsIFNjb3RsYW5kIGFuZCBOb3J0aGVybiBJcmVsYW5kLiBJIHdhbnRlZCB0aGlzIGd1aWRlIHRvIGluY2x1ZGUgYSBkZW1vIG9mIGhvdyB0byBtYWtlIGludGVyYWN0aXZlIGxpbmUgZ3JhcGhzIHdpdGggZ2VuZGVyIGlkZW50aXR5IGRhdGEsIGJ1dCB1bmZvcnR1bmF0ZWx5IGdpdmVuIHRoaXMgaXMgb25seSB0aGUgZmlyc3QgeWVhciB0aGF0IHRoZSBPTlMgaGFzIGNvbGxlY3RlZCB0aGlzIGRhdGEgdGhhdCB3YXMgbm90IHBvc3NpYmxlLiBTbyBJIGZvdW5kIGEgZGF0YXNldCBmcm9tIDIwMTUgd2hpY2ggaW52b2x2ZXMgZXhwZXJpbWVudGFsIHN0YXRpc3RpY3MgdGhhdCBoYXZlIGJlZW4gdXNlZCBpbiB0aGUgSW50ZWdyYXRlZCBIb3VzZWhvbGQgU3VydmV5LiBGb3IgbW9yZSBpbmZvLCB5b3UgY2FuIGNoZWNrIG91dCB0aGlzIFtPTlMgbGlua10oaHR0cHM6Ly93d3cub25zLmdvdi51ay9wZW9wbGVwb3B1bGF0aW9uYW5kY29tbXVuaXR5L2N1bHR1cmFsaWRlbnRpdHkvc2V4dWFsaXR5L2RhdGFzZXRzL3NleHVhbGlkZW50aXR5YnlhZ2Vncm91cGJ5Y291bnRyeSkuIAoKYGBge3J9CiMgTG9hZCBpbiBkYXRhc2V0CgpkZjMgPC0gcmVhZF9jc3YoJ0RhdGEvY2xlYW5lZF9zZXh1YWxpdHlfZGYuY3N2JykKYGBgCgpgYGB7cn0KIyBCcmllZiBnbGltcHNlIGF0IHVuZGVybHlpbmcgZGF0YSBzdHJ1Y3R1cmUKCmhlYWQoZGYzLCAxMCkKYGBgCgojIyBEYXRhIGNsZWFuaW5nCgpXaGVuIEkgZmlyc3QgZm91bmQgdGhpcyBkYXRhc2V0IGl0IHdhcyB2ZXJ5IG1lc3N5IGFuZCBmb3JtYXR0ZWQgdGVycmlibHksIHNvIEkgcGVyZm9ybWVkIHNvbWUgY2xlYW5pbmcgb24gaXQgaW4gYSBzZXBhcmF0ZSBqdXB5dGVyIG5vdGVib29rLCB0byBzYXZlIGNsdXR0ZXJpbmcgdGhpcyBvbmUgYW5kIGRpc3RyYWN0aW5nIGZyb20gdGhlIG1haW4gdHV0b3JpYWwuIElmIHlvdSdkIGxpa2UgdG8gc2VlIGhvdyBJIGNsZWFuZWQgaXQgdXAsIHBsZWFzZSBzZWUgdGhlIFsnRGF0YV9jbGVhbmluZ19zZXh1YWxpdHkuaXB5bmInXShEYXRhX2NsZWFuaW5nX3NleHVhbGl0eS5pcHluYikgbm90ZWJvb2suIAoKIyMgRGF0YSBwcmUtcHJvY2Vzc2luZwoKVGhlIG9ubHkgcHJlLXByb2Nlc3Npbmcgd2UncmUgZ29pbmcgdG8gZG8gaXMgc3Vic2V0IG91ciBkYXRhIGJ5IGNvdW50cnksIGFuZCBhbHNvIGNyZWF0ZSAyIHNlcGFyYXRlIGRhdGFzZXRzIGZvciBHZW5kZXIgPSBNZW4gYW5kIEdlbmRlciA9IFdvbWVuLiBJJ2xsIGV4cGxhaW4gd2h5IHRoaXMgc3RlcCBpcyBuZWVkZWQgc29vbi4gCgpgYGB7cn0KIyBGaWx0ZXIgZGF0YXNldCB0byBmb2N1cyBvbiBFbmdsYW5kCmVuZ2xhbmRfZGYgPC0gZGYzICU+JQogIGZpbHRlcihDb3VudHJ5ID09ICdFbmdsYW5kJykKYGBgCgpgYGB7cn0KIyBMZXQncyBjaGVjayBpdCB3b3JrZWQuLiAKCnVuaXF1ZShlbmdsYW5kX2RmJENvdW50cnkpCmBgYAoKYGBge3J9CiMgRnVydGhlciBmaWx0ZXIgZGF0YSBmb3IgZWFjaCBnZW5kZXIKCm1lbiA8LSBlbmdsYW5kX2RmICU+JSBmaWx0ZXIoR2VuZGVyID09ICJNZW4iKQp3b21lbiA8LSBlbmdsYW5kX2RmICU+JSBmaWx0ZXIoR2VuZGVyID09ICJXb21lbiIpCgojIExldCdzIGNoZWNrIGl0IHdvcmtlZAoKdW5pcXVlKG1lbiRHZW5kZXIpCnVuaXF1ZSh3b21lbiRHZW5kZXIpCmBgYAoKCiMjIEludGVyYWN0aXZlIGxpbmVncmFwaAoKQ3JlYXRpbmcgYSBzaW1wbGUgbGluZSBncmFwaCBpbiBwbG90bHkgaXMgcHJldHR5IGVhc3ksIGJ1dCB3aGVyZSBwbG90bHkgc3RydWdnbGVzIChpbiBSKSBpcyBpbiBoYW5kbGluZyBmYWNldCBwbG90cy4gQSBmYWNldCBwbG90IGlzIGEgdHlwZSBvZiB2aXN1YWxpc2F0aW9uIHRoYXQgZGl2aWRlcyBkYXRhIGludG8gc3VicGxvdHMgYmFzZWQgb24gY2F0ZWdvcmljYWwgdmFyaWFibGVzLiBXaGF0IEknZCBsaWtlIHRvIGRvIGlzIGNyZWF0ZSBhIGZhY2V0IHBsb3Qgb2Ygc2V4dWFsaXR5IHBlcmNlbnRhZ2VzIGluIEVuZ2xhbmQgKDIwMTAtMjAxNCkgd2l0aCBpbmRpdmlkdWFsIHN1YnBsb3RzIGZvciBvdXIgdHdvIGdlbmRlcnMuIFRoaXMgaXMgYWNoaWV2ZWQgZWFzaWx5IGluIFB5dGhvbiBkdWUgdG8gdGhlIHBsb3RseS5leHByZXNzIG1vZHVsZSwgd2hpY2ggcHJvdmlkZXMgYSBzaW1wbGUgd2F5IHRvIGNyZWF0ZSBmYWNldCBwbG90cy4gVW5mb3J0dW5hdGVseSwgd2UnbGwgaGF2ZSB0byBnbyB0aHJvdWdoIGEgYml0IG1vcmUgb2YgYSBsb25nd2luZGVkIHJvdXRlLCB3aGVyZSB3ZSdsbCBtYW51YWxseSBjcmVhdGUgb3VyIGluZGl2aWR1YWwgcGxvdHMgZm9yIGVhY2ggZ2VuZGVyLCB0aGVuIGNvbWJpbmUgdGhlbSB1c2luZyB0aGUgc3VicGxvdCBmdW5jdGlvbi4gQWxzbywgcGxvdGx5LmV4cHJlc3MgYXV0b21hdGljYWxseSBtYW5hZ2VzIGxlZ2VuZHMgdG8gZW5zdXJlIHRoZXkncmUgdW5pZmllZCBhY3Jvc3MgZmFjZXRzLCBidXQgUidzIHBsb3RseSByZXF1aXJlcyB0aGF0IHdlIG1hbnVhbGx5IHN5bmMgdXAgdGhlc2UgbGVnZW5kcy4gV29tcCB3b21wLiBMZXQncyBnZXQgdG8gaXQuIAoKCgpgYGB7cn0KIyBDcmVhdGUgaW5kaXZpZHVhbCBwbG90IGZvciBlYWNoIGdlbmRlcgoKIyBDcmVhdGUgcGxvdHMgZm9yIGVhY2ggZ2VuZGVyCm1lbl9wbG90IDwtIHBsb3RfbHkobWVuLCAKICAgICAgICAgICAgICAgICAgICB4ID0gflllYXIsIAogICAgICAgICAgICAgICAgICAgIHkgPSB+UGVyY2VudGFnZSwgCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+U2V4dWFsaXR5LCAKICAgICAgICAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCAKICAgICAgICAgICAgICAgICAgICAjIG1vZGUgdXNlZCB0byBtYWtlIHN1cmUgb3VyIGRhdGEgcG9pbnRzIGFyZSBjb25uZWN0ZWQgYnkgbGluZXMgYWNyb3NzIHRoZSB5ZWFycwogICAgICAgICAgICAgICAgICAgIG1vZGUgPSAnbGluZXMrbWFya2VycycsIAogICAgICAgICAgICAgICAgICAgIGhvdmVyaW5mbyA9ICd0ZXh0JywKICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gfnBhc3RlKCJZZWFyOiIsIFllYXIsICI8YnI+UGVyY2VudGFnZToiLCBQZXJjZW50YWdlLCAiPGJyPlNleHVhbGl0eToiLCBTZXh1YWxpdHkpLAogICAgICAgICAgICAgICAgICAgICMgbGVnZW5kZ3JvdXAgcGFyYW1ldGVyIGVuc3VyZXMgdGhhdCBkYXRhIHBvaW50cyByZWxhdGluZyB0byB0aGUgc2FtZSBjYXRlZ29yeSBhcmUgc3luY2VkIGFjcm9zcyBwbG90cwogICAgICAgICAgICAgICAgICAgIGxlZ2VuZGdyb3VwID0gflNleHVhbGl0eSwKICAgICAgICAgICAgICAgICAgICAjIHNob3dsZWdlbmQgcGFyYW1ldGVyIHNldCB0byBUUlVFIG9ubHkgZm9yIHRoaXMgcGxvdCB0byBhdm9pZCBkdXBsaWNhdGUgbGVnZW5kcwogICAgICAgICAgICAgICAgICAgIHNob3dsZWdlbmQgPSBUUlVFKSAlPiUKICBsYXlvdXQoeGF4aXMgPSBsaXN0KHRpdGxlID0gJ1llYXInLCB0aWNrdmFscyA9IDIwMTA6MjAxNCwgdGlja3RleHQgPSAyMDEwOjIwMTQpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnUGVyY2VudGFnZScpLAogICAgICAgICAjIEhlcmUgd2UgYWRkIGFuIGFubm90YXRpb24gdG8gdGhlIGdyYXBoIHRvIGxhYmVsIHRoZSBmaXJzdCBzdWJwbG90ICJNZW4iCiAgICAgICAgICMgU2V0dGluZyB4cmVmIGFuZCB5cmVmIHRvICdwYXBlcicgc2ltcGx5IG1lYW5zIHRoZSBhbm5vdGF0aW9uIHdvbid0IG1vdmUgaWYgd2Ugem9vbSBpbiBvciBvdXQKICAgICAgICAgYW5ub3RhdGlvbnMgPSBsaXN0KAogICAgICAgICAgIGxpc3QoeCA9IDAuNSwgeSA9IDEuMDUsIHRleHQgPSAiTWVuIiwgc2hvd2Fycm93ID0gRkFMU0UsIHhyZWY9J3BhcGVyJywgeXJlZj0ncGFwZXInKSkpCgoKd29tZW5fcGxvdCA8LSBwbG90X2x5KHdvbWVuLCAKICAgICAgICAgICAgICAgICAgICAgIHggPSB+WWVhciwgCiAgICAgICAgICAgICAgICAgICAgICB5ID0gflBlcmNlbnRhZ2UsIAogICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+U2V4dWFsaXR5LCAKICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIAogICAgICAgICAgICAgICAgICAgICAgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgCiAgICAgICAgICAgICAgICAgICAgICBob3ZlcmluZm8gPSAndGV4dCcsCiAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gfnBhc3RlKCJZZWFyOiIsIFllYXIsICI8YnI+UGVyY2VudGFnZToiLCBQZXJjZW50YWdlLCAiPGJyPlNleHVhbGl0eToiLCBTZXh1YWxpdHkpLAogICAgICAgICAgICAgICAgICAgICAgbGVnZW5kZ3JvdXAgPSB+U2V4dWFsaXR5LAogICAgICAgICAgICAgICAgICAgICAgc2hvd2xlZ2VuZCA9IEZBTFNFKSAlPiUKICBsYXlvdXQoeGF4aXMgPSBsaXN0KHRpdGxlID0gJ1llYXInLCB0aWNrdmFscyA9IDIwMTA6MjAxNCwgdGlja3RleHQgPSAyMDEwOjIwMTQpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnUGVyY2VudGFnZScpLAogICAgICAgICBhbm5vdGF0aW9ucyA9IGxpc3QoCiAgICAgICAgICAgbGlzdCh4ID0gMC41LCB5ID0gMS4wNSwgdGV4dCA9ICJXb21lbiIsIHNob3dhcnJvdyA9IEZBTFNFLCB4cmVmPSdwYXBlcicsIHlyZWY9J3BhcGVyJykpKQoKIyBMZXQncyB0YWtlIGEgbG9vayBhdCBvbmUgb2YgdGhlc2UgZ3JhcGhzCgp3b21lbl9wbG90CmBgYAoKYGBge3J9CiMgQ29tYmluZSBpbmRpdmlkdWFsIHBsb3RzIHVzaW5nIHN1YnBsb3QKIyBXaXRoaW4gc3VicGxvdCwgZGVmaW5lIG51bWJlciBvZiByb3dzLCBtYWtlIHN1cmUgc2hhcmUgc2FtZSB4IGF4ZXMgYW5kIGJvdGggYXhlcyB0aXRsZXMKZmlnNSA8LSBzdWJwbG90KG1lbl9wbG90LCB3b21lbl9wbG90LCBucm93cyA9IDIsIHNoYXJlWCA9IFRSVUUsIHRpdGxlWCA9IFRSVUUsIHRpdGxlWSA9IFRSVUUpICU+JQogIGxheW91dCgKICAgIHRpdGxlID0gbGlzdCgKICAgICAgdGV4dCA9ICdTZXh1YWxpdHkgUGVyY2VudGFnZXMgYnkgR2VuZGVyIGluIEVuZ2xhbmQgKDIwMTAtMjAxNCknLCAKICAgICAgeSA9IDAuOTgsICAjIE1vdmUgdGhlIHRpdGxlIGhpZ2hlciB1cAogICAgICB4ID0gMC41LCAgICMgQ2VudGVyIHRoZSB0aXRsZQogICAgICB4YW5jaG9yID0gImNlbnRlciIsCiAgICAgIHlhbmNob3IgPSAidG9wIgogICAgKSwKICAgIG1hcmdpbiA9IGxpc3QodCA9IDEwMCksICAjIEFkZCBzcGFjZSBhdCB0aGUgdG9wIGZvciB0aGUgdGl0bGUKICAgIGhlaWdodCA9IDgwMCwKICAgIHdpZHRoID0gMTAwMAogICkKCmZpZzUKYGBgCgojIFNoYXJpbmcgeW91ciBpbnRlcmFjdGl2ZSBncmFwaHMgb25saW5lIAoKSSdtIGdvaW5nIHRvIHByb3ZpZGUgeW91IGZpcnN0IHdpdGggYSByZWFsbHkgc2ltcGxlIHdheSB0byBob3N0IFBsb3RseSBncmFwaHMgc3BlY2lmaWNhbGx5LCB0aGVuIHdlJ2xsIGxvb2sgaW50byBvdGhlciBtb3JlIGNvbXBsZXggb3B0aW9ucyB0aGF0IHdvcmsgd2l0aCBtYW55IHZpc3VhbGlzYXRpb24gcGFja2FnZXMuCgoxLiBVc2UgUGxvdGx5J3MgWydDaGFydCBTdHVkaW8nXShodHRwczovL2NoYXJ0LXN0dWRpby5wbG90bHkuY29tLykuIFlvdSBjYW4gdXBsb2FkIHlvdXIgdmlzdWFsaXNhdGlvbnMgZGlyZWN0bHkgZnJvbSB5b3VyIGNvZGluZyBlbnZpcm9ubWVudCBhbmQgdGhlbiBnZXQgYSBsaW5rIHRvIHNoYXJlIHRoZW0gb25saW5lLiBZb3UnbGwgbmVlZCB0byBzaWduIHVwIGZvciBhbiBhY2NvdW50IGJ1dCBpdCdzIGZyZWUsIHVubGVzcyB5b3Ugd2FudCB0byBzaGFyZSB0aGUgbGluayBwcml2YXRlbHkgdGhlbiB5b3UnbGwgbmVlZCB0byB1cGdyYWRlIHlvdXIgYWNjb3VudC4gT3RoZXJ3aXNlLCBmb3IgZGF0YSB0aGF0J3MgZmluZSBiZWluZyBvdXQgaW4gdGhlIG9wZW4sIHRoaXMgaXMgYSBnb29kIG9wdGlvbi4KCjIuIEVtYmVkIHlvdXIgZ3JhcGhzIGluIEdpdEh1YiBwYWdlcy4KCgo=